Skip to content
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

The evaluation of the Keyof Type Operator produces different results to the Pick built-in tool. #48173

Closed
someBrown opened this issue Mar 8, 2022 · 7 comments
Labels
Question An issue which isn't directly actionable in code

Comments

@someBrown
Copy link

Bug Report

πŸ”Ž Search Terms

Pick

πŸ•— Version & Regression Information

v4.5.4

⏯ Playground Link

Link

πŸ’» Code

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

@jcalz
Copy link
Contributor

jcalz commented Mar 8, 2022

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.

@RyanCavanaugh RyanCavanaugh added the Question An issue which isn't directly actionable in code label Mar 8, 2022
@RyanCavanaugh
Copy link
Member

+1 to jcalz; this behaves exactly the same as a step-by-step manual evaluation of the type

@someBrown
Copy link
Author

someBrown commented Mar 8, 2022

new playground link

type Foo<T, U = keyof T> = {
  [P in Extract<U, keyof T>]-?: T[P]
}
type Res = Foo<User, 'name'>     //name: string | undefined;

Sorry, I forgot to add the '-?' operator in type Foo. I changed the name attribute to required, why does it still return undefined?

@fatcerberus
Copy link

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]>.

@ben-lau
Copy link

ben-lau commented Mar 9, 2022

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.

@jcalz
Copy link
Contributor

jcalz commented Mar 9, 2022

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 write undefined to u.name has to do with --exactOptionalPropertyTypes (#43947) but that doesn't change things here.

@ben-lau
Copy link

ben-lau commented Mar 9, 2022

All right, I missed this, thanks.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Question An issue which isn't directly actionable in code
Projects
None yet
Development

No branches or pull requests

5 participants