Skip to content

{ [K in keyof T]: T[K] } like type not work with generic type #33529

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
vipcxj opened this issue Sep 20, 2019 · 13 comments
Closed

{ [K in keyof T]: T[K] } like type not work with generic type #33529

vipcxj opened this issue Sep 20, 2019 · 13 comments
Labels
Working as Intended The behavior described is the intended behavior; this is not a bug

Comments

@vipcxj
Copy link

vipcxj commented Sep 20, 2019

TypeScript Version: 3.4.0-dev.201xxxxx

Search Terms:

Code

function test<T extends { a: string, b: string }>(obj: T) {
   const test: Partial<T> = { a: 'a', b: 'b' }; // TS2322 type '{ a: string, b: string }' is not assignable to type 'Partial<T>'.
}

Expected behavior:
no error

Actual behavior:
TS2322 error

Playground Link:

Related Issues:
#33524

@jack-williams
Copy link
Collaborator

This would be unsound if you instantiated T with a more specific type.

function test<T extends { a: string, b: string }>(obj: T) {
   const test: Partial<T> = { a: 'a', b: 'b' };
   return test.a
}

const result: 'b' | undefined = test({ a: 'b', b: 'a'} as const); // actually 'a'

@vipcxj
Copy link
Author

vipcxj commented Sep 20, 2019

@jack-williams what are you mean?

function test<T extends { a: string, b: string }>(obj: T) {
   const test: Partial<T> = { a: 'a', b: 'b' } as T; // force no error.
   return test.a
}
const result: 'b' | undefined = test({ a: 'b', b: 'a'} as const); // TS2322: Type '"a" | undefined' is not assignable to type '"b" | undefined'. Type '"a"' is not assignable to type '"b | undefined"'.

@jack-williams
Copy link
Collaborator

Can you create a playground link that shows that error?

@Siegrift
Copy link

@vipcxj What @jack-williams is trying to say is that the assignment doesn't work because the generic T parameter might be more specific. For example, if it worked you would be able to write code like this: http://www.typescriptlang.org/play/#code/JYOwLgpgTgZghgYwgAgEJwM4oN7LgLmQzClAHMAaZAI0ONJDOQF8BYAKA5gFcQExgAexDJIxADwAVZBAAekEABMMaTBAB8ACkHUAVoUkBKA8mwdkyKBDDcoI3AWQByOE6q1n1JywDcHNpzsoJCwiCgAwoIAtgAOADZyMvIQSiroWKbmyIJgABbQdCTk-hwcihAIcXBWyAlgyAjR8XIAanBx3BCEkbEJshx1eCA5+VA9zbJtHSgAvKIQxJqNva3tnYYcQA

@jcalz
Copy link
Contributor

jcalz commented Sep 20, 2019

Search Terms: 🦗🦗

Duplicate of #13442, #21196, etc

@RyanCavanaugh RyanCavanaugh added the Working as Intended The behavior described is the intended behavior; this is not a bug label Sep 20, 2019
@vipcxj
Copy link
Author

vipcxj commented Sep 20, 2019

type Mix<B, T extends B> = { [K in keyof T]: K extends keyof B ? B[K] : T[K] }
type PMix<B, T extends B> = { [K in keyof T]?: K extends keyof B ? B[K] : T[K] }

type Base = { a: string, b: string }
interface EX extends Base {
  a: '1';
}
const e: Mix<Base, EX> = { a: '3', b: '' } 
function test<T extends Base>(obj: T): string | undefined {
   const test: PMix<Base, T> = { a: 'a', b: 'b' }; // still not work here.
   return test.a
}
let result = test({ a: 'b', b: 'a'} as const);  // result is string | undefined now.
result = '3'

playground

@vipcxj
Copy link
Author

vipcxj commented Sep 23, 2019

@Siegrift PMix is another version of Partial, and test return PMix, not T, so test({ a: 'b', b: 'a', c: 'c', d: 'd', e: 'e'}) be assigned to { a?: string, b?: string, c: 'c', d: 'd', e: 'e'} should be ok.

@Siegrift
Copy link

Sorry, you're right. I don't know what is wrong with that code. I created even smaller example, but I don't get why it doesn't typecheck... (@jack-williams do you know why?)

playground link

@jack-williams
Copy link
Collaborator

@Siegrift Your example doesn't work because you are still assigning a concrete value to something that is inherently generic. The error is correct here for the same reasons:

const a: "b" | undefined = partial({ a: 'b' as 'b'}).a // no error, 'a' at runtime

@vipcxj Returning a value of type string | undefined does not negate the fact you are assigning to a generic value who's concrete type you do not know, and you do not know who else has a reference to that object (and at what type). TypeScript cannot go back an retroactively allow an assignment because later on the value is used in a potentially consistent way. Only using a generic in an input position and not in an output position is usually wrong.

@Siegrift
Copy link

@jack-williams Thank you very much. I wasn't able to create a counterexample.

@typescript-bot
Copy link
Collaborator

This issue has been marked 'Working as Intended' and has seen no recent activity. It has been automatically closed for house-keeping purposes.

@vipcxj
Copy link
Author

vipcxj commented Sep 26, 2019

@jack-williams please give me a example of PMix.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Working as Intended The behavior described is the intended behavior; this is not a bug
Projects
None yet
Development

No branches or pull requests

6 participants