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

keyof erasure in generic union types #18990

Closed
attatrol opened this issue Oct 6, 2017 · 3 comments
Closed

keyof erasure in generic union types #18990

attatrol opened this issue Oct 6, 2017 · 3 comments
Labels
Duplicate An existing issue was already created

Comments

@attatrol
Copy link

attatrol commented Oct 6, 2017

TypeScript Version: 2.2.2, 2.4.0

Code

interface A {
    a: number;
}

interface B extends A {
    b: number
}

export type IFieldConfig<TData> = { name: keyof TData };

export type TFieldConfigMap<TData> = {[P in keyof TData]: IFieldConfig<TData> & { name: P } };

let AFields: TFieldConfigMap<A> = {
    a: { name: 'a' }
};

let BFieldsFailed: TFieldConfigMap<B> = Object.assign({}, AFields, {
    b: { name: 'b' }
});

let BFieldsFailedToo: TFieldConfigMap<B> = Object.assign({}, AFields, {
    b: { name: <keyof B>'b' }
});

let BFieldsWorkaround: TFieldConfigMap<B> = Object.assign({}, AFields, {
    b: { name: <keyof Pick<B, 'b'>>'b' }
});

const BMap: {[P in keyof B]: P} = {
    a: 'a',
    b: 'b'
}

let BFieldsWorkaround2: TFieldConfigMap<B> = Object.assign({}, AFields, {
    b: { name: BMap.b }
});

Expected behavior:
compile flawlessly
Actual behavior:
failure to deduce types of both BFieldsFailed

Type 'TFieldConfigMap & { b: { name: string; }; }' is not assignable to type 'TFieldConfigMap'. Types of property 'b' are incompatible.
Type '{ name: string; }' is not assignable to type 'IFieldConfig & { name: "b"; }'.
Type '{ name: string; }' is not assignable to type 'IFieldConfig'.
Types of property 'name' are incompatible.
Type 'string' is not assignable to type '"a" | "b"'.

and BFieldsFailedToo

Type 'TFieldConfigMap & { b: { name: "a" | "b"; }; }' is not assignable to type 'TFieldConfigMap'.
Types of property 'b' are incompatible.
Type '{ name: "a" | "b"; }' is not assignable to type 'IFieldConfig & { name: "b"; }'.
Type '{ name: "a" | "b"; }' is not assignable to type '{ name: "b"; }'.
Types of property 'name' are incompatible.
Type '"a" | "b"' is not assignable to type '"b"'.
Type '"a"' is not assignable to type '"b"'.

I'm not sure if it is a bug in a second case.

@jcalz
Copy link
Contributor

jcalz commented Oct 11, 2017

If you want to assign something to TFieldConfigMap<B>, then the compiler wants to be sure that its b property is of type {name: 'b'}. In the first case, you are passing in the value { name: 'b' }, which is being inferred to be of type { name: string }:

let BFieldsFailed: TFieldConfigMap<B> = Object.assign({}, AFields, {
    b: { name: 'b' } // inferred as type { name: string };
});

So it warns you. The simplest fix here is to explicitly narrow the value to the type { name: 'b' }. This works:

let BFieldsOkay: TFieldConfigMap<B> = Object.assign({}, AFields, {
    b: { name: 'b' as 'b' }
});

Your second example uses keyof B which is 'a' | 'b'. That's not 'b', so the compiler is warning you.

Your third example is using keyof Pick<B, 'b'> which is a verbose way of saying 'b', and so it works.


So what bug do you think you're looking at? That automatic literal inference is not aggressive enough? I, myself, would like a way to tell TypeScript "infer the narrowest possible type, please" without having to redundantly write out literals as both values and types, as mentioned in #17943, but that's apparently closed as a design limitation. Is this a duplicate of #17943?

@attatrol
Copy link
Author

attatrol commented Oct 11, 2017

@jcalz

Is this a duplicate of #17943?

Yes, it is.

@mhegazy mhegazy added the Duplicate An existing issue was already created label Oct 11, 2017
@mhegazy
Copy link
Contributor

mhegazy commented Oct 25, 2017

Automatically closing this issue for housekeeping purposes. The issue labels indicate that it is unactionable at the moment or has already been addressed.

@mhegazy mhegazy closed this as completed Oct 25, 2017
@microsoft microsoft locked and limited conversation to collaborators Jun 14, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Duplicate An existing issue was already created
Projects
None yet
Development

No branches or pull requests

3 participants