Skip to content

Incorrect constructor overload: takes only first overload, but not others #60745

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

Closed
psnet opened this issue Dec 12, 2024 · 9 comments Β· Fixed by #60934
Closed

Incorrect constructor overload: takes only first overload, but not others #60745

psnet opened this issue Dec 12, 2024 · 9 comments Β· Fixed by #60934
Labels
Design Limitation Constraints of the existing architecture prevent this from being fixed

Comments

@psnet
Copy link

psnet commented Dec 12, 2024

πŸ”Ž Search Terms

constructor, typescript, Uint32Array, Uint16Array, Uint8Array, Uint32ArrayConstructor, Uint16ArrayConstructor, Uint8ArrayConstructor

πŸ•— Version & Regression Information

  • This changed between versions 5.6.3 and 5.7.2

⏯ Playground Link

https://www.typescriptlang.org/play/?target=9&ts=5.7.2#code/FAFwngDgpgBAkgOwCZQB4FEA2UC2UEgDOAwgPYKEgBOArgMYilUwC8MAqgJYEDMATAEEqVAIZgyFavUbMAPh24gAjADYho8eUq0GTGPK4EAHOrETt0pgG5gwAGY0EDTuRggolABR1SOCOXwQABVIKAAuGAQaHAAjKCoAGhg6AAtHAGsAEREQEQAhbgjTMDyaOzt4gEoYAG9gGAaYbBAYADdOKAB3EOgIxBQMbDwCEi0pXSobesbCTs4QVJhvX38EQJ6oarrGneSRQlgAdSgYgHEAGT4AJXwUKm4Acwl3VBAAOnYAOQBlOFPP9CZAD6cE+QTC012u3aXQ2rAUvEEwjEUyhUJiVCgInSqLRdH2RxOF2ut3ij2eaHeX1+-0BQO+AAkAPJXcGQtENGHdULwwzKNTIsC4jkNDFYnG2Dn4g4wY5nS43ZBkhBPcgvKk-P4A4F5ACaQXQEJFjS5cLYfJMguFIrF2OtOxQdhENEwICNxrcKSopE6kS6MHQwiYngABgAiHx+AIEDZhmCcQiRUgtalaumgoJJVO04GMllBGB6bPaoF6g1LAAkNUjq3WoQAvpUQ5UbDt65LGj5JHtMHQXTkoEhSuV4kzygcWmwACytztjHt9zADodlCpUc74B4gFLwozWzEgGhUBB+32m0LeNIILI5fLcJL43v99wrkdUMd2CcPkRPpcv4drhuKrbi2wDtkAA

πŸ’» Code

type IndexElementsConstructor = Uint32ArrayConstructor | Uint16ArrayConstructor | Uint8ArrayConstructor;

function test(componentType: number, chunkDataBin: ArrayBuffer) {
    let viewType: IndexElementsConstructor;

    switch (componentType) {
        case WebGL2RenderingContext.UNSIGNED_INT:
            viewType = Uint32Array;

            break;

        case WebGL2RenderingContext.UNSIGNED_SHORT:
            viewType = Uint16Array;

            break;

        case WebGL2RenderingContext.UNSIGNED_BYTE:
            viewType = Uint8Array;

            break;

        default:
            throw new Error(`"componentType" is not UNSIGNED_INT, UNSIGNED_SHORT or UNSIGNED_BYTE (${componentType})`);
    }

    const calculatedBufferOffset = 4;
    const calculatedBufferLength = 8;

    return new viewType(chunkDataBin, calculatedBufferOffset, calculatedBufferLength);
}

πŸ™ Actual behavior

Image

πŸ™‚ Expected behavior

Image

Additional information about the issue

If change code to simpler variant:

type IndexElementsConstructor = Uint32ArrayConstructor | Uint16ArrayConstructor | Uint8ArrayConstructor;

function test(componentType: number, chunkDataBin: ArrayBuffer) {
    const viewType: IndexElementsConstructor = Uint32Array;

    const calculatedBufferOffset = 4;
    const calculatedBufferLength = 8;

    return new viewType(chunkDataBin, calculatedBufferOffset, calculatedBufferLength);
}

newest TS version recognizes this.

@psnet
Copy link
Author

psnet commented Dec 12, 2024

As we can see 3rd overload:

Microsoft VS Code\resources\app\extensions\node_modules\typescript\lib\lib.es5.d.ts:

interface Uint32ArrayConstructor {
    readonly prototype: Uint32Array<ArrayBufferLike>;
    new (length: number): Uint32Array<ArrayBuffer>;
    new (array: ArrayLike<number>): Uint32Array<ArrayBuffer>;
    new <TArrayBuffer extends ArrayBufferLike = ArrayBuffer>(buffer: TArrayBuffer, byteOffset?: number, length?: number): Uint32Array<TArrayBuffer>;
    new (array: ArrayLike<number> | ArrayBuffer): Uint32Array<ArrayBuffer>;

    /**
     * The size in bytes of each element in the array.
     */
    readonly BYTES_PER_ELEMENT: number;

    /**
     * Returns a new array from a set of elements.
     * @param items A set of elements to include in the new array object.
     */
    of(...items: number[]): Uint32Array<ArrayBuffer>;

    /**
     * Creates an array from an array-like or iterable object.
     * @param arrayLike An array-like or iterable object to convert to an array.
     */
    from(arrayLike: ArrayLike<number>): Uint32Array<ArrayBuffer>;

    /**
     * Creates an array from an array-like or iterable object.
     * @param arrayLike An array-like or iterable object to convert to an array.
     * @param mapfn A mapping function to call on every element of the array.
     * @param thisArg Value of 'this' used to invoke the mapfn.
     */
    from<T>(arrayLike: ArrayLike<T>, mapfn: (v: T, k: number) => number, thisArg?: any): Uint32Array<ArrayBuffer>;
}
declare var Uint32Array: Uint32ArrayConstructor;

@jcalz
Copy link
Contributor

jcalz commented Dec 12, 2024

This isn't a bug in TS, but it's unfortunate fallout of the new feature whereby TypedArrays are generic over ArrayBufferLike as implemented in #59417. You're running afoul of one of the caveats in the the support for calling union types implemented in #29011:

Only one type in the list of union members is allowed to have overloads, and only one type within the list of union members is allowed to have type parameters

So since the new constructors are generic, they have type parameters, and things go sideways. They wanted #59417 not to be a breaking change but I think there are these edge cases where it happens. You should probably change IndexElementsConstructor to

type IndexElementsConstructor = new (buffer: ArrayBuffer, byteOffset?: number, length?: number) =>
    Uint32Array | Uint16Array | Uint8Array;

to make it work again.

Playground link to code

@RyanCavanaugh RyanCavanaugh added the Design Limitation Constraints of the existing architecture prevent this from being fixed label Dec 12, 2024
@typescript-bot
Copy link
Collaborator

This issue has been marked as "Design Limitation" and has seen no recent activity. It has been automatically closed for house-keeping purposes.

@typescript-bot typescript-bot closed this as not planned Won't fix, can't repro, duplicate, stale Dec 16, 2024
@psnet
Copy link
Author

psnet commented Dec 16, 2024

@jcalz thanks for explaining. Sadly that during #59417 were not implemented better solutions and when for union types caveats. Because of that all together code in the example above feels like tricky. Or you need to write code that will satisfy TS, code for TS, not for you 😒

@psnet
Copy link
Author

psnet commented Dec 16, 2024

Now if we want BYTES_PER_ELEMENT from

type IndexElementsConstructor = Uint32ArrayConstructor | Uint16ArrayConstructor | Uint8ArrayConstructor;

let viewType: IndexElementsConstructor;
...
viewType.BYTES_PER_ELEMENT

things become more complicated.

@psnet
Copy link
Author

psnet commented Dec 16, 2024

Marking not interesting issues with "Design Limitation" is very interesting tool for solving problems, is not it, @RyanCavanaugh ?

Somebody failed once long time ago, someone failed again now and we got mess, but instead of fix "we will close ticket and act like nothing happens"?

@rbuckton
Copy link
Member

rbuckton commented Jan 8, 2025

When you union constructor types, generic signatures must match exactly. Since each constructor type in the union produces a different return type, they are excluded from the resulting set. In the long term, I can look into adding non-generic overloads for these cases, assuming its feasible to do so without introducing further breaks. In the short term, @jcalz suggestion is a good workaround.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Design Limitation Constraints of the existing architecture prevent this from being fixed
Projects
None yet
Development

Successfully merging a pull request may close this issue.

6 participants