Skip to content
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

Unable to assign self to indirect self-referential union generic #50283

Closed
Seamooo opened this issue Aug 12, 2022 · 2 comments
Closed

Unable to assign self to indirect self-referential union generic #50283

Seamooo opened this issue Aug 12, 2022 · 2 comments

Comments

@Seamooo
Copy link

Seamooo commented Aug 12, 2022

Bug Report

πŸ”Ž Search Terms

union generics
self-referential generic
ndarray
recursive type references

πŸ•— Version & Regression Information

  • This is the behavior in every version I tried, and I reviewed the FAQ for entries about generics

⏯ Playground Link

Playground Link

πŸ’» Code

type FooType<T> = T[] | T;
type BarType<T> = FooType<FooType<T>>;
const bar: BarType<number> = [];
const foo: FooType<number> = [3];
bar.push(foo);

πŸ™ Actual behavior

When making use of an indirect self-referential union generic such as:

type FooType<T> = T[] | T;
type BarType<T> = FooType<FooType<T>>;

I'm unable to assign what I believe to be a reasonable pathway of the expansion

const bar: BarType<number> = [];
const foo: FooType<number> = [3];
bar.push(foo);

this should be the equivalent of FooType<number>[], however typescript disagrees that this assignment is valid
and only allows type number

Argument of type 'number[]' is not assignable to parameter of type 'number'.

I understand that the message is due to the number literal, but the typing is equivalent, as expressed
by the annotations.

Interestingly the single type version of this works, as demonstrated in (#33050)

type FooType<T> = T | FooType<T>[];

So it appears that the indirection is what's breaking this.

πŸ™‚ Expected behavior

I'm able to compile the above without error

@whzx5byb
Copy link

whzx5byb commented Aug 12, 2022

I don't see any unexpected behavior.

FooType<number> fully resolves to number[] | number, and BarType<number> fully resolves to number | number[] | (number | number[])[]. There is no self reference or circular reference here.

Because of CFA, when you assign const bar: BarType<number> = [], the actual type of bar is narrowed to number[] | (number | number[])[], so that you can call .push on it. Also note that function parameter type is contravariance, that means (M[] | N[]).push will only accept M & N. In your case it is number & (number | number[]), which resolves to number.

@Seamooo
Copy link
Author

Seamooo commented Aug 12, 2022

My mistake, closing

@Seamooo Seamooo closed this as completed Aug 12, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants