Reputation: 73
I'm new to TypeScript and I'm having a hard time understanding the following error.
Type '{ order: string; }[]' is not assignable to type 'TestType[]'.
Type '{ order: string; }' is not assignable to type 'TestType'.
Types of property 'order' are incompatible.
Type 'string' is not assignable to type 'number | ""'.
This is my test code
export enum ACTION_TYPES {
TEST_ACTION = 'TEST_ACTION',
}
export type TestType = {
order: number | '';
};
export function TestAction(
testTypes: TestType[]
): {
type: ACTION_TYPES.TEST_ACTION;
testTypes: TestType[];
} {
return {
type: ACTION_TYPES.TEST_ACTION,
testTypes,
};
}
export type PluginsState = {
testTypes: TestType[];
};
export type Actions =
| ReturnType< typeof TestAction >;
const reducer = (
state: PluginsState = {
testTypes: [],
},
payload?: Actions
): PluginsState => {
if ( payload && 'type' in payload ) {
switch ( payload.type ) {
case ACTION_TYPES.TEST_ACTION:
return {
...state,
testTypes: payload.testTypes,
};
}
}
return state;
};
export const stub = [
{
order: '',
}
];
const defaultState: PluginsState = {
testTypes: [],
};
reducer( defaultState, {
type: ACTION_TYPES.TEST_ACTION,
testTypes: stub,
} );
and this is TypeScript playground link
The error comes from the last line testTypes: stub
I'm pretty sure there is something I'm doing wrong, but I'm not understanding why ''
cannot be used with number | ""
Is there a workaround to this error?
Upvotes: 1
Views: 2257
Reputation: 55876
What you have written is correct from an author of the code perspective, this should be working fine. But, here is how Typescript sees it (rather, inferences it):
export const stub = [
{
order: '',
}
];
This is essentially, of type: {order:string}[]
, which makes sense in most of the cases. However, this is a generalized version of your {order:''}[]
so, Typescript cannot guarantee that whatever is passed is going to be {order: number|''}
, you may mutate it and Typescript will still have to allow it. So, it warns you.
To avoid this inferencing you can do two things:
Explicitly assign the type to stub
as TestType
so that TypeScript can error out when it is no more of that type, like this:
export const stub: TestType[] = [
{
order: "",
},
] ;
Or, perform a const assertion on the mutable array elements promising that element is unchanging like this:
export const stub = [
{
order: "",
} as const,
] ;
Here is your working example: https://tsplay.dev/WvpdYN
Upvotes: 1
Reputation: 329923
When you write
export const stub = [
{
order: ''
}
];
the compiler has to infer the type of stub
. There are all kinds of types that it could be (such as unknown
or any
or Array<object>
, etc). So the compiler uses heuristic rules to pick what it thinks is most appropriate. In the above case, the type inferred is:
/* const stub: Array<{order: string}> */
That's an unordered array of objects which must have an order
property of type string
. That's string
, and not the string
literal type ""
. Inferring string
instead of ""
is often exactly what people want in declarations like this: string-valued properties are often changed later:
stub[0].order = "somethingElse"; // okay
But unfortunately, that's not the type you wanted. When you later try to use it in as part of an argument you pass to reducer()
, the compiler sees {order: string}
as not assignable to TestType
, and you get an error.
In order to fix this you need to either explicitly annotate stub
's type to what you want:
export const stub: Array<TestType> = [ // annotated
{ order: '' }
];
reducer(defaultState, {
type: ACTION_TYPES.TEST_ACTION,
testTypes: stub, // okay
});
or you need to change how the value is inferred by the compiler, possibly by using a const
assertion to keep ""
as a string literal:
export const stub = [
{
order: '' as const, // const assertion
}
];
/* const stub: Array<{ order: ""; } */
reducer(defaultState, {
type: ACTION_TYPES.TEST_ACTION,
testTypes: stub,
});
Upvotes: 1