-
Notifications
You must be signed in to change notification settings - Fork 12.6k
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
Add Length Parameter to typed arrays #18471
Comments
you can already do this today with: type Vector2d = MyInt32Array<2>;
type Vector3d = MyInt32Array<3>;
type Matrix = MyInt32Array<16>;
interface MyInt32Array<T extends number> extends Int32Array {
length: T;
}
interface Int32ArrayConstructor {
new<T extends number>(length: T): MyInt32Array<T>;
} |
I am not sure of the utility of pushing this change into the standard library though. |
My only criticism of that suggestion is that it doesn't handle the accessing or setting of an index beyond the length. Well that and having to create custom array types to mimic the existing data types. |
Array is not handled differently in the type system. so it will be strange if we start paying attention to |
A custom type guard (like @mhegazy's above) is definitely a good immediate solution, but I think it would be a huge improvement for TypeScript to specifically track the For normal arrays, this would expand the scope of type-checking significantly, since array lengths can be modified after creation. However, typed arrays have fixed sizes at creation and can then be treated as a unique primitive. Current ExampleHere's an obvious bug which isn't currently caught at compile time: function specialTransform (array: Uint8Array) {
if (array.length !== 4) {
throw new Error('This function is only for 4-byte Uint8Arrays.')
}
return new Uint8Array([array[0] + 1, array[2], array[3] - 1, array[4]])
}
const bytes = new Uint8Array([1, 2, 3]);
// Will always throw an error at runtime, but can't be identified at compile time:
specialTransform(bytes); This code could never work, and the thrown error in This could be made harder-to-mess-up with a custom type guard: interface SpecialType extends Uint8Array {
readonly length: 4;
}
export function brandSpecialType(array: Uint8Array): SpecialType {
if (array.length !== 4) {
throw new Error('This function is only for 4-byte Uint8Arrays.')
}
return array as SpecialType;
}
function specialTransform (array: specialType) {
return Uint8Array([array[0] + 1, array[2], array[3] - 1, array[4]])
}
// the user still needs to
const bytes = Uint8Array([1, 2, 3]);
// Runtime error happens a little earlier, but still not at compile time:
const mySpecialType = brandSpecialType(bytes);
specialTransform(mySpecialType); This is marginally better in some situations, but requires a lot more custom infrastructure. If I'm writing a library that operates on sets of specifically sized typed arrays (like a cryptographic library), I can't provide both flexibility and strict typing. (See (Please let me know if I'm missing a better solution here.) I believe I can either allow the developer to pass my functions a simple What I would like to seeIMO, it would be ideal if TypeScript had the ability to track the length of typed arrays, and check them using simple expressions. Something like: function specialTransform (array: Uint8Array<length === 4>) {
return new Uint8Array([array[0] + 1, array[2], array[3] - 1, array[4]])
}
// works
specialTransform(new Uint8Array([1, 2, 3, 4]));
// ts error: Argument of type 'Uint8Array<{length: 3}>' is not assignable to parameter of type 'Uint8Array<length === 4>'.
specialTransform(new Uint8Array([1, 2, 3])); Along with allowing certain examples to be fully type-checked, this would allow significantly better interoperability between libraries which deal with typed arrays. (Last note: I'm sure there's a more general feature that could be implemented and used to add this functionality to TypedArrays, but I'm not knowledgable enough to discuss the general case. I think it may be similar to the |
IMO, it would also be useful to be able to enforce non-zero array length. |
This feature would be very useful now that |
Actually, it is possible to do that with recent Typescript versions: type FixedSizeArray<T, N extends number> = N extends N ? number extends N ? T[] : _FixedSizeArray<T, N, []> : never;
type _FixedSizeArray<T, N extends number, R extends unknown[]> = R['length'] extends N ? R : _FixedSizeArray<T, N, [T, ...R]>;
type KnownKeys<T> = {
[K in keyof T]: string extends K ? never : number extends K ? never : K
} extends { [_ in keyof T]: infer U } ? U : never;
type Float32ArrayWithoutIndex = Pick<Float32Array, KnownKeys<Float32Array>>;
type FixedSizeFloat32Array<N extends number> = FixedSizeArray<number, N> & Float32ArrayWithoutIndex; ...but it is cumbersome and feels a bit hacky, it would be wonderful if Typescript would provide first-hand support for the compiler to see typed arrays as fixed-size arrays. |
We at Datadog had this problem with type Vec2 = [x: number, y: number] | Float32Array;
const drawPoint([x, y]: Vec2) {
console.log(x + y);
// with `noUncheckedIndexedAccess`, x and y are now `number | undefined`!
} We patched the We considered hacks like |
The goal is pretty straight forward, since typed arrays have a fixed length it would be a more accurate and helpful type it you could specify length. Then you could get complaints if you try to assign or access a higher index or use a array of the incorrect length.
Code
The text was updated successfully, but these errors were encountered: