question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Aliasing a type in a conditional type changes the result

See original GitHub issue

Bug Report

🔎 Search Terms

any, alias, conditional type,

🕗 Version & Regression Information

All versions are effected.

⏯ Playground Link

Playground Link

💻 Code

interface User {
    id: number;
    username: string;
}

type Q<T> = {a: keyof T & string };

type t1 = Q<any> extends Q<User> ? true : never;

type qAny = Q<any>;
type t2 = qAny extends Q<User> ? true : never;

🙁 Actual behavior

t1 is true, and t2 is never.

🙂 Expected behavior

Both, t1 and t2 should be the same and be never, because {a: string} extends {a: 'id' | 'username'} is false. The only difference is that the left value of extends is for t2 an alias, but the same type as in t1.

Interesting might be that t2 was in v4.1 true as well, but started to get the right value in v4.2.

Maybe related to https://github.com/microsoft/TypeScript/issues/31295

Issue Analytics

  • State:open
  • Created a year ago
  • Comments:5 (4 by maintainers)

github_iconTop GitHub Comments

3reactions
tjjfvicommented, May 3, 2022

I believe this boils down to an unsound variance check. TS detects Q<T> as contravariant, since keyof is contravariant, and thus simplifies the check of Q<any> extends Q<User> to User extends any, which is obviously true. However, keyof is not always contravariant; for example:

type A = { x: 1, y?: 2 }
type B = { x: 1 }

const p: A = null! as B; // ok, B is assignable to A
const q: keyof B = null! as keyof A; // error, keyof A is not assignable to keyof B

type Q<T> = { x: keyof T };
type T1 = Q<A> extends Q<B> ? true : never; // true
type QA = Q<A>;
type T2 = QA extends Q<B> ? true : never; // never

T2 is never because the intermediate type alias causes the instantiation of Q<A>, making the extends use a structural comparison instead of the unsound variance comparison.

1reaction
andrewbranchcommented, May 3, 2022

IIUC #48070, or at least the draft fix that Anders put up, is specific to signatures. I think @tjjfvi nailed it. @ahejlsberg thoughts / triage help?

Read more comments on GitHub >

github_iconTop Results From Across the Web

Conditional types change behaviour if under type alias - Reddit
Conditional types change behaviour if under type alias. I recently wanted to make sure that an array of strings contained all the strings...
Read more >
Aliasing and Mutability
List Values Can Be Changed. Unlike the previous types we've worked with, the values in a list can be changed directly, without creating...
Read more >
Documentation - Conditional Types - TypeScript
Create types which act like if statements in the type system. ... Conditional types help describe the relation between the types of inputs...
Read more >
Typescript: Type alias results in strange type when returning ...
The printing of types in typescript is not something that is well defined and changes frequently based on feedback. In most cases typescript ......
Read more >
Type alias, alias template (since C++11) - cppreference.com
It does not introduce a new type and it cannot change the meaning of an existing type name. There is no difference between...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found