microsoft / microsoft/TypeScript

Covariance breaks when checking for `undefinded` in a type

Offen
#55,161 3 Kommentare 0 Reaktionen 1 zugewiesene Person Auf GitHub ansehen

@weswigham arbeitet bereits daran.

Seit 27.7.2023.

Needs Investigation
Vorherrschende Sprache
Go
Sterne
111k
Forks
14.3k
Ø Merge
1 T. 19 Std.
Gemergte PRs (30 T.)
117

Beschreibung

Bug Report

I'm using the solid-js npm package which defines a type called Setter:

type Setter<T> = (undefined extends T ? () => undefined : {}) & (<U extends T>(value: (prev: T) => U) => U) & (<U extends T>(value: Exclude<U, Function>) => U) & (<U extends T>(value: Exclude<U, Function> | ((prev: T) => U)) => U);

I don't fully understand the reasoning behind it, but it's giving me some problems:

type Setter<T> = (undefined extends T ? () => undefined : {}) & (<U extends T>(value: (prev: T) => U) => U) & (<U extends T>(value: Exclude<U, Function>) => U) & (<U extends T>(value: Exclude<U, Function> | ((prev: T) => U)) => U);

class A { a = 1 }
class B extends A { b = 2 }

type Test<T> = { 0: Setter<T> };

declare const a: Test<A>;
const b: Test<B> = a; // ERROR!

I created an object with the property 0 which contains a Setter and it appears to not be covariant.
Inspecting the error it looks like that the problem is originated from this part undefined extends T ? () => undefined : {}, infact if I remove it this works, moreover the problem would still occur if that was the only part of the type:

type Setter<T> = undefined extends T ? () => undefined : {};

class A { a = 1 }
class B extends A { b = 2 }

type Test<T> = { 0: Setter<T> };

declare const a: Test<A>;
const b: Test<B> = a; // ERROR!

I think it is an error because the types are the same:

type TA = Test<A>;
//   ^? type TA = { 0: {}; }
type TB = Test<B>;
//   ^? type TB = { 0: {}; }

Additionally if Test were to be defined as [ Setter<T> ] the error would not occur:

type Setter<T> = undefined extends T ? () => undefined : {};

class A { a = 1 }
class B extends A { b = 2 }

type Test<T> = [ Setter<T> ];

declare const a: Test<A>;
const b: Test<B> = a; // OK?!?!
🔎 Search Terms
  • undefined extends T
  • covariance
  • covariant
🕗 Version & Regression Information

This changed between versions 3.3.3 and 3.5.1. (The only playground version in which it doesn't occur is 3.3.3)

⏯ Playground Link

Playground Link

💻 Code
type Setter<T> = (undefined extends T ? () => undefined : {}) & (<U extends T>(value: (prev: T) => U) => U) & (<U extends T>(value: Exclude<U, Function>) => U) & (<U extends T>(value: Exclude<U, Function> | ((prev: T) => U)) => U);
// type Setter<T> = (<U extends T>(value: (prev: T) => U) => U) & (<U extends T>(value: Exclude<U, Function>) => U) & (<U extends T>(value: Exclude<U, Function> | ((prev: T) => U)) => U);
// type Setter<T> = undefined extends T ? () => undefined : {};

class A { a = 1 }
class B extends A { b = 2 }

type Test<T> = { 0: Setter<T> };
// type Test<T> = [ Setter<T> ];

declare const a: Test<A>;
const b: Test<B> = a; // ERROR!

type TA = Test<A>;
//   ^?
type TB = Test<B>;
//   ^?
🙁 Actual behavior

Can't assign b with the value of a.
It is wrong at the very least because it is not consistent

🙂 Expected behavior

Can assign b with the value of a

Beitragsleitfaden

Beitragsleitfaden öffnen

Erste Schritte

  1. Lies das ganze Issue und danach den Beitragsleitfaden des Projekts.
  2. Schreib ins Issue, dass du es übernimmst — das erspart doppelte Arbeit.
  3. Forke das Repository und arbeite in einem Branch.
  4. Öffne einen Pull Request, der die Issue-Nummer nennt.

Bewertung

Dieses Issue wurde noch nicht bewertet.

Neue Issues direkt in Ihr Postfach

Eine kurze Übersicht über anfängerfreundliche GitHub-Issues.