I'm having trouble using object spread in Typescript functions with Type variables.
Is it possible at all (as of now)? If not, what are concise alternatives?
Here is what I observed with both Typescript v2.6 and v2.7-dev:
In the function definitions below, the ok ones compile just fine but the err ones give the following compiler error:
TS2698: Spread types may only be created from object types.
interface IMessages {
[msgKey: string]: string;
}
const ok1 = () => {
type TFieldNames = "a" | "b" | "c";
const fieldErrors: { [field in TFieldNames]?: IMessages } = {};
const fieldName = "XXX" as TFieldNames;
const otherMessages = fieldErrors[fieldName]; // has type IMessages
fieldErrors[fieldName] = { ...otherMessages, too_big: "is too big" };
};
const ok2 = () => {
type TFieldNames = keyof { a: number; b: number; c: number };
const fieldErrors: { [field in TFieldNames]?: IMessages } = {};
const fieldName = "XXX" as TFieldNames;
const otherMessages = fieldErrors[fieldName]; // has type IMessages
fieldErrors[fieldName] = { ...otherMessages, too_big: "is too big" };
};
const ok3 = () => {
type TFieldNames = string;
const fieldErrors: { [field in TFieldNames]?: IMessages } = {};
const fieldName = "XXX" as TFieldNames;
const otherMessages = fieldErrors[fieldName]; // has type "any"
fieldErrors[fieldName] = { ...otherMessages, too_big: "is too big" };
};
const err1 = <TFieldNames extends string>() => {
const fieldErrors: { [field in TFieldNames]?: IMessages } = {};
const fieldName = "XXX" as TFieldNames;
const otherMessages = fieldErrors[fieldName]; // has type "any"
fieldErrors[fieldName] = { ...otherMessages, too_big: "is too big" };
};
const err2 = <TFields extends { [key: string]: any }>() => {
const fieldErrors: { [field in keyof TFields]?: IMessages } = {};
const fieldName = "XXX" as keyof TFields;
const otherMessages = fieldErrors[fieldName]; // has type "any"
fieldErrors[fieldName] = { ...otherMessages, too_big: "is too big" };
};
const err3 = <TFields extends object>() => {
const fieldErrors: { [field in keyof TFields]?: IMessages } = {};
const fieldName = "XXX" as keyof TFields;
const otherMessages = fieldErrors[fieldName]; // has type "any"
fieldErrors[fieldName] = { ...otherMessages, too_big: "is too big" };
};
If you look at the inferred type in typescript playground for otherMessages in err1, it's
const otherMessages: { [field in TFieldNames]?: IMessages; }[TFieldNames]
And it starts working if you add explicit type for otherMessages:
const err1b = <TFieldNames extends string>() => {
const fieldErrors: { [field in TFieldNames]?: IMessages } = {};
const fieldName = "XXX" as TFieldNames;
const otherMessages: IMessages | undefined = fieldErrors[fieldName];
fieldErrors[fieldName] = { ...otherMessages, too_big: "is too big" };
};
It seems that typescript on its own is unable to simplify { [field in TFieldNames]?: IMessages; }[TFieldNames] to IMessages | undefined. Looks like a bug.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With