const string typescript literals
I was defining constants to be used as Redux action types and was surprised to see that constant strings had type string!
My suggestion is that variables declared with the const keyword should have the most specific type possible, which is, in the case of strings and numbers, literals.
Please, either make constant strings and numbers be literals by default or provide a keyword or operator which can be used to force the string or number to have the literal type
const helloWorldActionType = '@@reducerName/helloWorld';
const goodbyeWorldActionType = '@@reducerName/goobyeWorld';
interface IAction<T extends string,P> {
type: T;
payload: P;
}
function helloWorld(name: string): IAction<typeof helloWorldActionType, string> {
return {
type: helloWorldActionType,
payload: `Hello world! I'm ${name}.`
}
}
function goobyeWorld(name: string): IAction<typeof goodbyeWorldActionType, number> {
return {
type: goodbyeWorldActionType,
payload: name.length
}
}
type Actions = ReturnType<typeof helloWorld | typeof goobyeWorld>;
const reducer: Reducer<ThisReducersStateType,Actions> = (state, action) => {
switch(action.type) {
case helloWorldActionType:
// this case doesn't make 'action' be considered as of type 'IAction<typeof helloWorldActionType, string>' because 'helloWorldActionType' is of type 'string'
const str: string = action.payload; // "action.payload" is still "string | number", which creates an error...
// ...
}
This can be fixed with
const helloWorldActionType = '@@reducerName/helloWorld' as '@@reducerName/helloWorld';
but it makes no sense... helloWorldActionType should be of type '@@reducerName/helloWorld' instead of string without the cast because it's a constant and can never change to another string other than that one.
This is annoying because I really don't want to have to type the string twice (not DRY), once in the value and again in the cast, every time I create a constant string.
My suggestion meets these guidelines:
I just found a trick for this but it creates useless clutter in the runtime...
function makeLiteral<L extends string>(s: L): L {
return s;
}
and then do
const helloWorldActionType = makeLiteral('@@reducerName/helloWorld');
I'm going to use this for now but I really hope you guys can find a way to do this in a cleaner way!
Thank you in advance!
I just found a trick for this but it creates useless clutter in the runtime...
There is a cleaner way to do this:
const helloWorldActionType = '@@reducerName/helloWorld' as const;
See https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-4.html#const-assertions
Uhm ok! That's nice and way better. Thanks.
But don't you still think const strings and numbers should automatically be literals?
I think it is? Not sure what TS version you're on, but in the current playground const helloWorldActionType = '@@reducerName/helloWorld' has type "@@reducerName/helloWorld". Which makes my previous bit of advice pointless for this case.
Yeah, I can't reproduce this issue either.
I do see that the "suggestion" issue template doesn't ask for TS version or a Playground link, but maybe it should?
provide a keyword or operator
"string" as const
Most helpful comment
"string" as const