Typescript: const number and strings make automatic literals

Created on 2 May 2019  路  6Comments  路  Source: microsoft/TypeScript

Search Terms

const string typescript literals

Suggestion

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

Use Cases

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.

Examples

Checklist

My suggestion meets these guidelines:

  • [x] This wouldn't be a breaking change in existing TypeScript/JavaScript code (every code which declares a constant string or number can't change the value later on so there is no problem in TypeScript now considering such variables as literals)
  • [x] This wouldn't change the runtime behavior of existing JavaScript code
  • [x] This could be implemented without emitting different JS based on the types of the expressions
  • [x] This isn't a runtime feature (e.g. library functionality, non-ECMAScript syntax with JavaScript output, etc.)
  • [x] This feature would agree with the rest of TypeScript's Design Goals.
Question

Most helpful comment

provide a keyword or operator

"string" as const

All 6 comments

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

Was this page helpful?
0 / 5 - 0 ratings