Skip to content

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

Closed

Description

πŸ”Ž 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.

Activity

  1. psnet commented on Dec 12, 2024

    @psnet
    Author

    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;
  2. jcalz commented on Dec 12, 2024

    @jcalz
    Contributor

    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

  3. typescript-bot commented on Dec 16, 2024

    @typescript-bot
    Contributor

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

  4. psnet commented on Dec 16, 2024

    @psnet
    Author

    Joe Calzaretta (@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 😒

  5. psnet commented on Dec 16, 2024

    @psnet
    Author

    Now if we want BYTES_PER_ELEMENT from

    type IndexElementsConstructor = Uint32ArrayConstructor | Uint16ArrayConstructor | Uint8ArrayConstructor;
    
    let viewType: IndexElementsConstructor;
    ...
    viewType.BYTES_PER_ELEMENT

    things become more complicated.

  6. psnet commented on Dec 16, 2024

    @psnet
    Author

    Marking not interesting issues with "Design Limitation" is very interesting tool for solving problems, is not it, Ryan Cavanaugh (@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"?

  7. rbuckton commented on Jan 8, 2025

    @rbuckton
    Contributor

    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, Joe Calzaretta (@jcalz) suggestion is a good workaround.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Design LimitationConstraints of the existing architecture prevent this from being fixed

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions