TypeScript Version: 2.9.1
Search Terms:
keyof,optional
Code
export type Map<T = {}> = {
readonly [P in Extract<keyof T, string>]: A<P>;
};
export interface A<TNameType extends string = string> {
test: string;
}
export interface B {
required: string;
optional?: string;
}
export const Test: Map<B> = {
required: 'required',
}
Expected behavior:
No errors should be reported, because an optional property should not be required as a result of keyof.
Actual behavior:
Type '{ required: string; }' is not assignable to type 'Map<B>'.
Property 'optional' is missing in type '{ required: string; }'.
It was working in 2.8.3, without Extract<>
Playground Link:
Playground Link
Related Issues:
This is pretty unfortunate - our workaround to only include string keys was to use Extract<keyof T, string>, but mapped types that use that trick are no longer considered homomorphic mapped types, which means that we can't preserve modifiers on properties.
Is there a reason you only want to include string keys?
I think if you use a layer of indirection you can get the desired homomorphicity back (homomorphicness? homomorphologicality?). Specifically, make a new type parameter K extends keyof T, assign Extract<keyof T, string> to it, and then map over that. Like this:
export type Map<T = {}, K extends keyof T = Extract<keyof T, string>> = {
readonly [P in K]: A<P>;
};
or, equivalently
type OnlyStringKeyProps<T> = Pick<T, Extract<keyof T, string>>;
export type Map<T = {}> = {
readonly [P in keyof OnlyStringKeyProps<T>]: A<P>;
};
Then the following should work (note that the type of the required property had to be modified to type {test: string} as defined in A<> ; I assume OP wasn't meaning to make this error):
export const Test: Map<B> = {
required: { test: 'required' },
}; // no error now
So at least there's a workaround? Not sure if this is the way to do it going forward, or if there are other ideas.
Automatically closing this issue for housekeeping purposes. The issue labels indicate that it is unactionable at the moment or has already been addressed.
Most helpful comment
I think if you use a layer of indirection you can get the desired homomorphicity back (homomorphicness? homomorphologicality?). Specifically, make a new type parameter
K extends keyof T, assignExtract<keyof T, string>to it, and then map over that. Like this:or, equivalently
Then the following should work (note that the type of the
requiredproperty had to be modified to type{test: string}as defined inA<>; I assume OP wasn't meaning to make this error):So at least there's a workaround? Not sure if this is the way to do it going forward, or if there are other ideas.