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

Inconsistent behaivor between Pick<T,K> and [P in K]: T[K] #28414

Closed
weichensw opened this issue Nov 8, 2018 · 3 comments
Closed

Inconsistent behaivor between Pick<T,K> and [P in K]: T[K] #28414

weichensw opened this issue Nov 8, 2018 · 3 comments
Labels
Domain: Conditional Types The issue relates to conditional types Domain: Mapped Types The issue relates to mapped types Needs Investigation This issue needs a team member to investigate its status.

Comments

@weichensw
Copy link

TypeScript Version: 3.2.0-dev.20181107

Search Terms:
Pick Inconsistent readonly optional

Code

interface T {
    a: string;
    b?: string;
    c: string;
    readonly d: string;
    readonly e?: string;
}
type Test = Pick<T, Exclude<keyof T, 'c'>>;
type Test1 = { [P in Exclude<keyof T, 'c'>]: T[P]; };

Expected behavior:
Test1 should be equal to Test, as it is just an inline version of it

Actual behavior:
Test1 removed any optional or readonly on properties, while Test doesn't.

Playground Link:
Playground

Related Issues:
#14295

@jack-williams
Copy link
Collaborator

I'm afraid I can't help fix this issue, but here are some more examples that might help diagnose the problem.

interface T {
    a: string;
    b?: string;
    c: string;
    readonly d: string;
    readonly e?: string;
}

type Alias<T> = T extends unknown ? keyof T : never;
type Mapped<T> = { [P in (T extends unknown ? keyof T : never)]: T[P] };
type MappedKeyof<T> = { [P in (keyof T extends unknown ? keyof T : never)]: T[P] };
type MappedAlias<T> = { [P in Alias<T>]: T[P] };
type A = Mapped<T>; // does not have '?'
type B = MappedKeyof<T>; // has '?'
type C = MappedAlias<T>; // does not have '?'

I find the difference between A and B especially odd.

@weswigham weswigham added Needs Investigation This issue needs a team member to investigate its status. Domain: Mapped Types The issue relates to mapped types Domain: Conditional Types The issue relates to conditional types labels Nov 8, 2018
@jcalz
Copy link
Contributor

jcalz commented Nov 11, 2018

Yeah, the only way I know to get homomorphic mapped types over a subset of the keys of a type is to use a type alias featuring both the type parameter X and another parameter Y constrained to keyof X, and then map over Y:

type Foo<Y extends keyof X, X> = {[P in Y]: Bar<P>};

type Homomorphic = Foo<"a", {readonly a?: string}>
// type Homomorphic = {readonly a?: Bar<"a">}

Looks like that was specifically done to make Pick<T, K> work, according to #14295 and its linked issues / pull requests.

I don't know how you'd make the inline version work the same way, without possibly making other mapped types suddenly become homomorphic with respect to some unexpected types. You'd need to make {[P in Baz<T>]: Bar<P>} divine from Baz<T> what type to copy the modifiers from. That's obvious if Baz<T> is literally keyof T, and is arguably obvious-ish if it's like Exclude<keyof T, "c">, but what do you do with keyof T & keyof U or keyof T | keyof U or X extends Y ? keyof T : keyof U etc?

@weichensw
Copy link
Author

Thanks for the insight @jcalz. That make sense. Closing the issue.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Domain: Conditional Types The issue relates to conditional types Domain: Mapped Types The issue relates to mapped types Needs Investigation This issue needs a team member to investigate its status.
Projects
None yet
Development

No branches or pull requests

4 participants