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

Cannot assign generic type aliases that should be equivalent. #40312

Open
CyrusNajmabadi opened this issue Aug 29, 2020 · 8 comments
Open

Cannot assign generic type aliases that should be equivalent. #40312

CyrusNajmabadi opened this issue Aug 29, 2020 · 8 comments
Assignees
Labels
Bug A bug in TypeScript Fix Available A PR has been opened for this issue
Milestone

Comments

@CyrusNajmabadi
Copy link
Contributor

Cannot assign generic type aliases that should be equivalent.

Simple test case:

export type Example<TData>
    = (data: TData | null | undefined) => string | null;

function foo<T>()
{
    let x: Example<T> = undefined!;
    let y: Example<T | null | undefined> = undefined!;

    y = x;

    let x1: (data: T | null | undefined) => string | null = undefined!;
    let y1: (data: (T | null | undefined) | null | undefined) => string | null = undefined!;

    y1 = x1
}

Note that 'x' is not assignable to 'y' with strictNullChecks on. It results in the error:

Type 'Example<T>' is not assignable to type 'Example<T | null | undefined>'.
  Type 'T | null | undefined' is not assignable to type 'T'.
    'T' could be instantiated with an arbitrary type which could be unrelated to 'T | null | undefined'.
      Type 'undefined' is not assignable to type 'T'.
        'T' could be instantiated with an arbitrary type which could be unrelated to 'undefined'.(2322)

This seems very strange though. Example is just a type alias, which shouldn't have an impact on assignability. If we simply expand the type alias, we get the x1/y1 case. In the expanded form the compiler allows this just fine.

Note: this was working in 3.3. It does not seem to work on 4.0. I'm not sure at what point it may have broken.

@CyrusNajmabadi
Copy link
Contributor Author

Tagging @DanielRosenwasser

@DanielRosenwasser
Copy link
Member

Honestly, I'm kind of surprised we never ran into this earlier.

@jack-williams
Copy link
Collaborator

jack-williams commented Aug 29, 2020

My guess would be that this changed after #29817.

@DanielRosenwasser
Copy link
Member

CC @ahejlsberg @weswigham

@DanielRosenwasser DanielRosenwasser added Bug A bug in TypeScript Needs Investigation This issue needs a team member to investigate its status. labels Sep 1, 2020
@weswigham weswigham added Fix Available A PR has been opened for this issue and removed Needs Investigation This issue needs a team member to investigate its status. labels Sep 2, 2020
@RyanCavanaugh RyanCavanaugh added this to the Backlog milestone Sep 11, 2020
@AIVASteph
Copy link

AIVASteph commented Jan 14, 2021

Piping in! I had a similar issue occurring in Typescript 3.9 and 4.1.3 (playground) and 4.2.0-dev (playground). I've got a type called GetKeysOfType that creates a union of all possible keys of an object whose types are the same as the provided type. This works perfectly as when I type something with it the values that I can assign to it are scoped correctly.
However when I try to use this narrowed type to look-up into a map I get an error.

type GetKeysOfType<O, T extends O[keyof O]> = { -readonly [P in keyof O]: O[P] extends T ? P : never }[keyof O];

class BDocument<T, I extends T[keyof T]> {
}

class Collection<T, I extends T[keyof T]> {
    index!: GetKeysOfType<T, I>;
    indexToDoc = new Map<I, BDocument<T, I>>();

    async get() {
        var val: T; //Presume that this value is retrieved from a remote and is correctly typed.
        this.indexToDoc.get(val[this.index]); //This is where the error occurs.
    }
}

image

Playground link!

@TheDocTrier
Copy link

I am not sure if this is an instance of the same underlying bug, but I have run into a succinct typing problem which effects primitve types specifically:

// specify type directly
declare let x: { [K in keyof string]: Promise<string[K]> };
x.length; // has type `Promise<number>`

// specify type using a type alias
type L<T> = { [K in keyof T]: Promise<T[K]> };

declare let y: L<string>;
y.length; // has type `number`

declare let z: L<{ length: number }>;
z.length; // has type `Promise<number>`

Perhaps there is some limitation on generic type parameters that I am not aware of? If this is indeed intended behavior, it seems confusing. This was tested in the playground with 4.2.3 and 4.3.0-nightly.

@RyanCavanaugh
Copy link
Member

Linking in another repro

interface A<T> {
    field : T | null;
}
type assignTo = A<string[]>;
interface assignToExtended extends A<string[]> {}

function foo () : A<null> {
    return {
        field : null
    };
}

// This consistently fails on all versions. (and I think this is intended)
const a : assignTo = foo();

// This used to work, but now starts to fail from 4.3.1-rc
const b : assignToExtended = foo();

@tychenjiajun
Copy link

Have a similar issue, not sure whether they are same or not.

https://www.typescriptlang.org/play?target=99&ts=4.2.3#code/KYDwDg9gTgLgBDAnmYcAiBDGGCSA7AE1DgF44BnGKASzwHM4AfOPAVwFsAjYKJuKYBgIQ8AG0RwAFJRr0+bLjwCUAbQC6AbgBQWpCnRZchUAEEoUDBLIChI8VJm0GzBdyirNO0JFgJkqAGFgUVEAURDqMHJqcgAVf1I4AG8KAAsIAHdY6hhRYAB+AC44TggIPIw8OABfPlLywTxtXQScclCQDABjXMQTPEQAHliAPkT6iqrQGGBCcilYuGnZgnm8YAA3Hjh8hChWVGKAMwxRcmAlHb2DuGPT8+a9VA6qbph8IhBBgCVgLugCPEUGMyC8LD1Blo4NDklCYfCVABrYASWhwMFvQbIxAQI5wX7-KCA-wAGgoVCc8g4bhGamK2KWIBmcxYm22u3UtzgbQ6b3E-SGBIBQOASJRajGyxZVBuHOxZIAdErMNgPqZzJY1FyxYhFcrDGqQGYLIgRT8-sL-DqJZ54dUVBiIdjcfiLUSRWTHHIXNSeLSSXDoTZhGIJNIKd6WL73OotCNHgkAKp4DIWMDDY2M5mrfiCEP2cOyZxRxQxiWJWKZqU5lJ5egwVLFAAMNUDV1j8OKrm21fmlfMKgA5HW6A3B2o27t+1A28VC5SfaWPFmVn3M1PM4U29O28xJNOVyza7NR424ABGVvw+Eb8xtztwA+95JwEcN4oAJiv1+hHOnKibNQJx-aFiifJlVzgFQlQVWgjm2RMyVYPBETwTI8GAkDdkTQ8c2DOwwy9YtuzLK5k1TDB00TMZig7EDaLUJQE30FUjE+Y1LDNIV3X8EFuXaToen5AZzUJYlgVw+YZWAScoMw0DXTEkVJPJItZJUEj5K5bjxNQZ8dMGIiqVLMlKkQEZZPItNBkdd5jC+HSRRGCyH0chJnzMujfygki9QVKzKJsplwTsz5RMtFANOjCVaVnOTmNQVjDS4t1dL435bFDQYkvsjjTX8cKeOBSUIKPV8T3fOAW1qXYcs+bS8wI7KDVyjV8pQQq0rGZgMvzIY6vVE0UqU3iRgAicWn0AIIHYMA8hARIUgwYpzzJThPzJLpigAZjJAhikHAAWQcyWAYoUiOc64DoA7z0HGoHuqBKEGAShEmm2b5odYLMWdPEPrm0BPQjYjo1pZ6TESAaQDNZDUPQ+NJtQAAhKGWs+M0NggagCERp44ACNHVXss0zLxhI0CJtjQDNFRVrgD8JW0fHQip5KCoMoikJQtCMjwZzmYSAAxNmSY51LDJBsksZxgWkbgABxUWMfFsTJaLUyBjl-GAAllZp1WAXVpxgaLbXWn1mHDaJY36DJEjzf0AApS2zQB+byf0ABpV2Cs8pmdCOZCemoERH1emBOqcyRLiSNt-jwN6YGKaHSa1xI6a1DB5lT620u0eF8YyGA8TIP6DGJlWOrc4FtGqLQgA

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Bug A bug in TypeScript Fix Available A PR has been opened for this issue
Projects
None yet
8 participants