Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I hint to the Typescript compiler to infer string literal types for properties?

Tags:

typescript

The Typescript compiler will infer a string literal type for consts:

const a = 'abc';
const b: 'abc' = a; // okay, a is of type 'abc' rather than string

However, for properties, the type is inferred to be string.

const x = {
    y: 'def',
};

const z: { y: 'def' } = x; // error because x.y is of type string

In this example, how can I get the compiler to infer that x is of type { y: 'def' } without writing a type annotation for x?

Edit: There's an open issue requesting support for this feature. One suggested workaround is to use syntax like this:

const x = new class {
    readonly y: 'def';
};

const z: { readonly y: 'def' } = x; // Works

Try it in Playground here.

Edit 2: There's even an open PR that would solve this problem. Disabling type widening seems to be a popular request.

like image 895
Sam Avatar asked Dec 02 '17 08:12

Sam


People also ask

How do you type infer TypeScript?

Using infer in TypeScript It does that by first checking whether your type argument ( T ) is a function, and in the process of checking, the return type is made into a variable, infer R , and returned if the check succeeds: type ReturnType<T> = T extends (... args: any[]) => infer R ?

What is string literal type in TypeScript?

The string literal type allows you to specify a set of possible string values for a variable, only those string values can be assigned to a variable. TypeScript throws a compile-time error if one tries to assign a value to the variable that isn't defined by the string literal type.

What is type assertion in TypeScript?

In Typescript, Type assertion is a technique that informs the compiler about the type of a variable. Type assertion is similar to typecasting but it doesn't reconstruct code. You can use type assertion to specify a value's type and tell the compiler not to deduce it.


2 Answers

I think you're looking for the const assertion, added in TS 3.4.

You just need to add as const to the string for it to become a literal type.

const x = {
    y: 'def' as const,
};

const z: { y: 'def' } = x; // no error :)

TS playground link

like image 79
Daniel Reina Avatar answered Sep 28 '22 10:09

Daniel Reina


The difference is there is no const keyword for properties. Since there is no way to be sure the properties won't be mutated TS cannot assume a constant string literal, it has to assume the more generic string.

Try replacing the first const in your example with let and at that location too TS is going to assume string and not 'abc':

let a = 'abc';
const b: 'abc' = a; 

TS Playground link for this code

Is going to show an error for b "Type string is not assignable to type 'abc'".

Since TS cannot infer immutability from a language feature, as you do in your const variables example, the only way is to tell it that the obejct property is immutable is via an explicit type annotation, meaning the answer to your question is a negative.

like image 32
Mörre Avatar answered Sep 28 '22 09:09

Mörre