Skip to content

A behavior of conditional types is changed (regression) #30047

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
falsandtru opened this issue Feb 22, 2019 · 2 comments
Open

A behavior of conditional types is changed (regression) #30047

falsandtru opened this issue Feb 22, 2019 · 2 comments
Assignees
Labels
Needs Investigation This issue needs a team member to investigate its status.

Comments

@falsandtru
Copy link
Contributor

falsandtru commented Feb 22, 2019

@RyanCavanaugh @weswigham @ahejlsberg Caused by #27697

TypeScript Version: 3.4.0-dev.20190222

Search Terms:

Code

type Attrs = Record<string, string | EventListener | null | undefined>;
type Children =
  | Children.Void
  | Children.Text
  | Children.Collection
  | Children.Record;
namespace Children {
  export type Void = undefined;
  export type Text = string;
  export interface Collection extends ReadonlyArray<El> { }
  export type Record = { [field: string]: El; };
}
interface El<
  T extends string = string,
  E extends Element = Element,
  C extends Children = Children,
  > {
  readonly tag?: T;
  readonly element: E;
  children: Relax<C>;
}
type Relax<C extends Children> = C extends Children.Text ? Children.Text : C;

declare function f<C extends Children>(              children: C): C;
declare function f                    (attrs: Attrs,            ): void;
declare function f<C extends Children>(attrs: Attrs, children: C): C;
f({ a: 0 as any as El<'a', HTMLAnchorElement> }); // ok
f({ a: 0 as any as El<'a', HTMLAnchorElement, Children> }); // ok
f({ a: 0 as any as El<'a', HTMLAnchorElement, Children.Void> }); // error since 3.4.0-dev.20190222
f({ a: 0 as any as El<'a', HTMLAnchorElement, Children.Text> }); // error since 3.4.0-dev.20190222

Expected behavior:

pass

Actual behavior:

error

Playground Link: http://www.typescriptlang.org/play/index.html#src=type%20Attrs%20%3D%20Record%3Cstring%2C%20string%20%7C%20EventListener%20%7C%20null%20%7C%20undefined%3E%3B%0D%0Atype%20Children%20%3D%0D%0A%20%20%7C%20Children.Void%0D%0A%20%20%7C%20Children.Text%0D%0A%20%20%7C%20Children.Collection%0D%0A%20%20%7C%20Children.Record%3B%0D%0Anamespace%20Children%20%7B%0D%0A%20%20export%20type%20Void%20%3D%20undefined%3B%0D%0A%20%20export%20type%20Text%20%3D%20string%3B%0D%0A%20%20export%20interface%20Collection%20extends%20ReadonlyArray%3CEl%3E%20%7B%20%7D%0D%0A%20%20export%20type%20Record%20%3D%20%7B%20%5Bfield%3A%20string%5D%3A%20El%3B%20%7D%3B%0D%0A%7D%0D%0Ainterface%20El%3C%0D%0A%20%20T%20extends%20string%20%3D%20string%2C%0D%0A%20%20E%20extends%20Element%20%3D%20Element%2C%0D%0A%20%20C%20extends%20Children%20%3D%20Children%2C%0D%0A%20%20%3E%20%7B%0D%0A%20%20readonly%20tag%3F%3A%20T%3B%0D%0A%20%20readonly%20element%3A%20E%3B%0D%0A%20%20children%3A%20Relax%3CC%3E%3B%0D%0A%7D%0D%0Atype%20Relax%3CC%20extends%20Children%3E%20%3D%20C%20extends%20Children.Text%20%3F%20Children.Text%20%3A%20C%3B%0D%0A%0D%0Adeclare%20function%20f%3CC%20extends%20Children%3E(%20%20%20%20%20%20%20%20%20%20%20%20%20%20children%3A%20C)%3A%20C%3B%0D%0Adeclare%20function%20f%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20(attrs%3A%20Attrs%2C%20%20%20%20%20%20%20%20%20%20%20%20)%3A%20void%3B%0D%0Adeclare%20function%20f%3CC%20extends%20Children%3E(attrs%3A%20Attrs%2C%20children%3A%20C)%3A%20C%3B%0D%0Af(%7B%20a%3A%200%20as%20any%20as%20El%3C'a'%2C%20HTMLAnchorElement%3E%20%7D)%3B%0D%0Af(%7B%20a%3A%200%20as%20any%20as%20El%3C'a'%2C%20HTMLAnchorElement%2C%20Children%3E%20%7D)%3B%0D%0Af(%7B%20a%3A%200%20as%20any%20as%20El%3C'a'%2C%20HTMLAnchorElement%2C%20Children.Void%3E%20%7D)%3B

Related Issues:

@falsandtru
Copy link
Contributor Author

#30010 may fix this.

@RyanCavanaugh RyanCavanaugh added the Needs Investigation This issue needs a team member to investigate its status. label Feb 27, 2019
@jack-williams
Copy link
Collaborator

jack-williams commented Feb 27, 2019

Another smaller repro:

interface Foo<T> {
    prop: T extends unknown ? true : false;
}

const foo = { prop: true } as const;
const x: Foo<1> = foo;
const y: Foo<number> = foo;
const z: Foo<number> = x; // error

A smaller repro.

type Children =
    | Children.Void
    | Children.Text
    | Children.Collection
    | Children.Record;

namespace Children {
    export type Void = undefined;
    export type Text = string;
    export interface Collection extends ReadonlyArray<El> { }
    export type Record = { [field: string]: El; };
}

interface El<
    C extends Children = Children,
    > {
        children: Relax<C>;
    }

type Relax<C extends Children> = C extends Children.Text ? Children.Text : C;

const a: El = 1 as any as El<Children.Void>; // error

I think the issue here is that variance markers are marking C in Relax as invariant. This is probably caused by #27697 and the extension to variance computation.

Even though TypeScript never ends up relating two conditional types in a way the requires invariance of the check types, the effect of variance computation makes the assignment illegal.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Needs Investigation This issue needs a team member to investigate its status.
Projects
None yet
Development

No branches or pull requests

4 participants