You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
The evaluation of the Keyof Type Operator produces different results to the Pick built-in tool.Β #48173
interface User {
name?: string
age?: number
address?: string
}
type Foo<T, U = keyof T> = {
[P in Extract<U, keyof T>]: T[P]
}
type Bar<T,U=keyof T> = Required<Pick<T, Extract<U, keyof T>>>
type Res = Foo<User, 'name'> //name: string | undefined;
type Res2 = Bar<User, 'name'> //name: string
// We can quickly address your report if:// - The code sample is short. Nearly all TypeScript bugs can be demonstrated in 20-30 lines of code!// - It doesn't use external libraries. These are often issues with the type definitions rather than TypeScript bugs.// - The incorrectness of the behavior is readily apparent from reading the sample.// Reports are slower to investigate if:// - We have to pare too much extraneous code.// - We have to clone a large repo and validate that the problem isn't elsewhere.// - The sample is confusing or doesn't clearly demonstrate what's wrong.
π Actual behavior
type Res = Foo<User, 'name'> //name: string | undefined;
The value evaluated for the name contains undefined
π Expected behavior
type Foo<T, U = keyof T> = {
[P in Extract<U, keyof T>]: T[P]
}
[P in Extract<U, keyof T>]: T[P] should not return undefined because Extract only narrows down the range of the keys
I donβt see a bug here. The type User["name"] is string | undefined, so thatβs the type of the name property of Foo<User, "name">. Homomorphic mapped types and mapped type modifiers can complicate this, but neither of those are happening in Foo.
User["name"] evaluates to string | undefined because the property is optional. -? removes optionality in the new type, but this is separate from the evaluation of T[P], so you end up with name: string | undefined. You might consider NonNullable<T[P]>.
But there is another question: I think the ? symbol means this property is optional. So in this case, the name which is the keyof Users should be optional, but if you got this property , it has to be string not undefined as well.
If you have a value u of type User, then in const n = u.name, the type of n is string | undefined. That's just how it works. Whether or not the key is present vs absent and whether or not you can actually writeundefined to u.name has to do with --exactOptionalPropertyTypes (#43947) but that doesn't change things here.
Activity
jcalz commentedon Mar 8, 2022
I donβt see a bug here. The type
User["name"]isstring | undefined, so thatβs the type of thenameproperty ofFoo<User, "name">. Homomorphic mapped types and mapped type modifiers can complicate this, but neither of those are happening inFoo.RyanCavanaugh commentedon Mar 8, 2022
+1 to jcalz; this behaves exactly the same as a step-by-step manual evaluation of the type
someBrown commentedon Mar 8, 2022
new playground link
Sorry, I forgot to add the '-?' operator in type Foo. I changed the name attribute to required, why does it still return undefined?
fatcerberus commentedon Mar 8, 2022
User["name"]evaluates tostring | undefinedbecause the property is optional.-?removes optionality in the new type, but this is separate from the evaluation ofT[P], so you end up withname: string | undefined. You might considerNonNullable<T[P]>.ben-lau commentedon Mar 9, 2022
But there is another question: I think the
?symbol means this property is optional. So in this case, thenamewhich is the keyofUsersshould be optional, but if you got this property , it has to bestringnotundefinedas well.jcalz commentedon Mar 9, 2022
If you have a value
uof typeUser, then inconst n = u.name, the type ofnisstring | undefined. That's just how it works. Whether or not the key is present vs absent and whether or not you can actually writeundefinedtou.namehas to do with--exactOptionalPropertyTypes(#43947) but that doesn't change things here.ben-lau commentedon Mar 9, 2022
All right, I missed this, thanks.