Dennis Kats
Dennis Kats

Reputation: 2299

Why doesn't TypeScript infer the most narrow type declaration?

Why doesn't TypeScript emit "a" | "b" for the type of test2? My IDE's IntelliSense correctly infers that test2 is of type of "a" | "b", but the emitted type declaration is just "string".

// index.ts
export const test1 = true ? ('a' as const) : ('b' as const);
export const test2 = true ? 'a' : 'b';
// index.d.ts
export declare const test1: "a" | "b";
export declare const test2: string;
// tsconfig.json
{
  "compilerOptions": {
    "composite": true,
    "emitDeclarationOnly": true,
  }
}

StackBlitz Link

Upvotes: 0

Views: 67

Answers (1)

Remo H. Jansen
Remo H. Jansen

Reputation: 25029

Using type and typeof tp debug it:

export const test1 = true ? ('a' as const) : ('b' as const);
type T1 = typeof test1; // 'a' | 'b'
export const test2 = true ? 'a' : 'b';
type T2 = typeof test2; // 'a' | 'b'

In both cases, the type is 'a' | 'b'. Is TypeScript ignoring true ? in the conditional expression. It is simply inferring the type In both sides of the expression 'a' : 'b'.

Because string is compatible with "a" | "b" and you don't use as const, TypeScript will use string. This is done to prevent compatibility issues with libraries. This is not obvious in your example. but take a look at how it works when you use objects:

export const test1 = {
    a: 'a',
    b: 'b'
};

type T1 = typeof test1; // { a: string, b: string } 


export const test2 = {
    a: 'a',
    b: 'b'
} as const;

type T2 = typeof test2; // { a: 'a', b: 'b' } 

It is the same, but as you can imagine, using 'a' | 'b' in object values instead of string by default could lead to many issues in existing libraries; this is why unless you use as const, TypeScript will go for string.

Upvotes: 0

Related Questions