Reputation: 67380
To the best of my understanding TypeScript views a const string
variable as an immutable typed variable with only that value and no other possible value. I always thought that adding as const
to that was redundant.
Why am I getting the following in the 2nd part of the example?
Argument of type 'string' is not assignable to parameter of type...
Example:
declare function each<T extends [any] | any[]>(cases: ReadonlyArray<T>): (name: string, fn: (...args: T) => any, timeout?: number) => void;
const foo1 = 'FOO' as const;
const bar1 = 'BAR' as const;
declare function action1(value: typeof foo1 | typeof bar1): void;
each([
[foo1],
])('test name', (value) => {
// okay
action1(value);
});
const foo2 = 'FOO';
const bar2 = 'BAR';
declare function action2(value: typeof foo2 | typeof bar2): void;
each([
[foo2],
])('test name', (value) => {
// Argument of type 'string' is not assignable to parameter of type '"FOO" | "BAR"'.(2345)
action2(value);
});
Playground sample above is here.
Upvotes: 8
Views: 3700
Reputation: 3230
Don't confuse const
as a keyword for declaring immutable variables (same in JS by the way) with as const
, which called const assertions.
Const assertions behaves slightly different depending on the type (see attached link to the documentation), but in case of literal types it basically means that it cannot be extended (or widened) to string
. It narrows it to that specific literal type, which signals to compiler not to accept string
and this is the exact error you are getting.
Upvotes: 3
Reputation: 33111
TS is able to infer all types. You just should give a little bit of love )))
Just add extra generics to help TS figure out all types. TS is smart enough to figure out that const foo='FOO'
is constant even without as const
.
declare function each<U extends string, V extends U[], T extends V>(cases: ReadonlyArray<T>): (name: string, fn: (...args: T) => any, timeout?: number) => void;
const foo1 = 'FOO' as const;
const bar1 = 'BAR' as const;
declare function action1(value: typeof foo1 | typeof bar1): void;
each([
[foo1],
])('test name', (value) => {
action1(value);
});
const foo2 = 'FOO';
const bar2 = 'BAR';
declare function action2(value: typeof foo2 | typeof bar2): void;
each([
[foo2],
])('test name', value => action2(value)); // ok
As you see, I added extra U and V generics.
Please keep in mind, if you can't infer some type, try to add more generics )) This rule works 80% of time )
Why am I getting "Argument of type 'string' is not assignable to parameter of type..." in the second part of the example?
This is because value
is infered to string. action2
expects only foo
or bar
. Because string
type is much wider than foo | bar
, TS complains
Upvotes: 0
Reputation: 19193
Even if the first part of your question (with as const) does not warn about any error, it still does not work as each
method is not compiled to javascript code and therefore the execution fails.
Although your example tries to address an issue with how const behaves during narrowing I think it is too complicated. You probably will find useful the following suggestion issue on typescript project
Upvotes: 0