I would have expected a TypedArray
interface to exist in
the built-in declaration libraries. Instead, there are independent types for
Int8Array
, etc, with no common base type.
interface Int8Array { /* ... */ }
interface Int8ArrayConstructor { /* ... */ }
declare const Int8Array: Int8ArrayConstructor;
interface Uint8Array { /* ... */ }
interface Uint8ArrayConstructor { /* ... */ }
declare const Uint8Array: Uint8ArrayConstructor;
// ...
It seems sensible that there should be a common TypedArray
type, both because
TypedArray
, andinterface TypedArray { /* ... */ }
interface Int8Array extends TypedArray { }
interface Int8ArrayConstructor { /* ... */ }
declare const Int8Array: Int8ArrayConstructor;
interface Uint8Array extends TypedArray { }
interface Uint8ArrayConstructor { /* ... */ }
declare const Uint8Array: Uint8ArrayConstructor;
// ...
Is there a good reason why there is no TypedArray
type? If not, I can submit a PR.
Consider the isTypedArray()
function from lodash.
Currently, it is declared as follows:
function isTypedArray(value: any): boolean;
This proposal would enable an appropriate type-guard, without declaring
a convoluted union type:
function isTypedArray(value: any): value is TypedArray;
You can achieve the same behavior today by defining:
type TypedArray = Int8Array | Uint8Array | Int16Array | Uint16Array | Int32Array | Uint32Array | Uint8ClampedArray | Float32Array | Float64Array;
but in general define one TypedArray interface and use this
types should reduce the duplication.
@mhegazy Yes, that's the solution I'm using now, but it seems a bit unnecessary. I'll submit a PR shortly.
Would asking for types for 0x, 0b, and 0o be a different request on this? The reason I would like these type is to use with TypedArrays. Currently there is no guard on inserting any number into those typedArrays. Which shouldn't be allowed. Lets say you wanted to put a value 255 into a Int8Array field, this should have a type guard for
type int8 = -0x80 to 0x7F | -0b10000000 to 0b1111111 | -0o200 to 0o177
type hex = any 0xFFFFF...;
type binary = any 0b1001010101...;
type octal = any 0o17235...;
Something that would prevent a programmer from inserting numbers that will be converted within the typed arrays.
@marcusjwhelan That is a very different proposal, and far beyond the scope of this one. This is just about adding another interface to the declaration files, not about introducing new syntax.
There already is ArrayBufferView interface in lib.d.ts for this exact purpose, which is a supertype of all typed arrays.
interface ArrayBufferView {
/**
* The ArrayBuffer instance referenced by the array.
*/
buffer: ArrayBuffer;
/**
* The length in bytes of the array.
*/
byteLength: number;
/**
* The offset in bytes of the array.
*/
byteOffset: number;
}
interface ArrayBufferConstructor {
readonly prototype: ArrayBuffer;
new (byteLength: number): ArrayBuffer;
isView(arg: any): arg is ArrayBufferView;
}
declare const ArrayBuffer: ArrayBufferConstructor;
ArrayBuffer.isView(new Uint8Array()); // true
On Web IDL:
typedef (Int8Array or Int16Array or Int32Array or
Uint8Array or Uint16Array or Uint32Array or Uint8ClampedArray or
Float32Array or Float64Array or DataView) ArrayBufferView;
The solution that @mhegazy gives might be okay in some cases. But I'm writing code that is something like this:
type Arr<T> = T[] | Int8Array | Uint8Array | Int16Array | Uint16Array | Int32Array | Uint32Array | Uint8ClampedArray | Float32Array | Float64Array;
function isSame<T>(first: Arr<T>, second: Arr<T>) {
return first.every((ele: T, index: number): boolean => {
return ele === second[index];
});
}
And it just will not compile because every has this type def (it is copied for each type of array):
every(callbackfn: (this: void, value: number, index: number, array: Float64Array) => boolean): boolean;
FWIW, I have this concept in my library as I have to deal with TypedArrays a lot, see:
https://github.com/stardazed/stardazed/blob/master/src/core/array.ts#L36
I split it into a base and mutable and readonly extended interfaces, code as added works only in 2.4+ as the SharedArrayBuffer type was modified in the std declarations.
I also made a TS fork of gl-matrix (https://github.com/stardazed/veclib) where I often had to deal with TypedArray or array inputs being returned and wanting TS to deduce the right return type, solved like so:
type AN = ArrayOfNumber; // an ArrayLike<number> with non-readonly subscript
type ACN = ArrayOfConstNumber; // ArrayLike<number>
export function min(out: number[], a: ACN, b: ACN): number[];
export function min<T extends AN>(out: T, a: ACN, b: ACN): T;
export function min(out: AN, a: ACN, b: ACN) { ...code... }
This allows it to return the correct type info for both:
const a = min([], [1,2,3], [3,2,1]); // a: number[]
const b = min(new Int32Array(3), [1,2,3], [3,2,1]); // b: Int32Array
It seems like @saschanaz has the correct answer, and this issue can be closed
Wait, just kidding:
Property 'length' does not exist on type 'ArrayBufferView'.
Unfortunately, I think adding an interface for TypedArray might be a bad idea. TypedArray is a weird JavaScript constructor, which is why I think TypeScript is correct to avoid implementing a regular interface for it. As an alternative, I suggest adding length: number;
to the ArrayBufferView interface.
I found this thread after I read about TypedArrays on MDN then tried to write instanceof TypedArray
, which gave me a TypeScript error. After (erroneously) thinking this was a problem with TypeScript, I tried the same statement in plain JavaScript, and promptly got a JavaScript error too.
However, TypedArray _is_ a constructor, it's just not (usually) a _visible_ one, which is why new
and instanceof
won't work with it. To test this, I created a new Int16Array()
in Chrome, and checked its prototype chain (logging count, Object.prototype.toString.call(obj), obj.constructor.name, obj):
1 "[object Object]" "Int16Array" TypedArray {constructor: 茠, BYTES_PER_ELEMENT: 2}
2 "[object Object]" "TypedArray" {constructor: 茠,聽鈥
3 "[object Object]" "Object" {constructor: 茠, __defineGetter__: 茠, __defineSetter__: 茠, hasOwnProperty: 茠, __lookupGetter__: 茠,聽鈥
According to MDN "There is no global property named TypedArray, nor is there a directly visible TypedArray constructor." (I admit I don't fully understand what it means to be an "invisible" constructor. I understand why JavaScript doesn't allow new TypedArray
, because TypedArray only exists to centralize the common properties of a family of related constructors, and therefor shouldn't be instantiated by itself. But why disallow checking instanceof TypedArray
? TypedArray clearly _is_ in the prototype chain. Is it not possible for JavaScript to disallow one without the other?)
I might argue that it would make more sense for JavaScript to implement TypedArray as an normal constructor, the wayBlob
is File
's parent, but also a normal constructor itself. Here's the prototype chain for a new File()
:
1 "[object File]" "File" File {constructor: 茠,聽鈥
2 "[object Blob]" "Blob" Blob {slice: 茠, constructor: 茠, Symbol(Symbol.toStringTag): "Blob"}
3 "[object Object]" "Object" {constructor: 茠, __defineGetter__: 茠, __defineSetter__: 茠, hasOwnProperty: 茠, __lookupGetter__: 茠,聽鈥
So Blob
is to File
as TypedArray
is to Int16Array
, but because Blob
is a normal constructor, it's much more intuitive to work with. Running instanceof TypedArray
on a Int16Array
results in an error, whereas running instanceof Blob
on a File returns true
, exactly as you would expect.
However, until JavaScript makes TypedArray a normal constructor, I don't think TypeScript should implement an interface for it. Doing so might confuse users, who would then expect TypedArray to behave like other JavaScript constructors, which it does not.
As an alternative, I recommend adding length: number;
to the existing ArrayBufferView interface. This small change would give users a single, complete, generic interface they could use for all TypedArrays, while reenforcing (because of its different name) that it is just a TypeScript interface, and should _not_ be used as a JavaScript constructor.
I get why we should not write something like UIntArray extends TypedArray
because there is no constructor but why should we avoid adding an interface for it? An interface does not require a constructor.
Writing something like UIntArray implements TypedArray
does not automatically mean that we can call new TypedArray()
.
Using a common interface for TypedArrays would remove redundant declaration and would not imply that TypedArray
is a constructor i.e. can be used for instanceof
. It would also help in several type definitions that require TypedArray
as a type e.g node type declarations.
ArrayBufferView might be a solution but I'm not familiar with Web IDL and I never seen this type before. I always saw DataView | TypedArray
. It is also currently defined as an interface in lib.d.ts and not as a union type as described in the ArrayBufferView doc.
ArrayBufferView
has no property length
! This is a bug, right? Specs
Only byteLength
is available. On a Int16Array(2)
length
is 2
and byteLength
4
.
length
just returns the count of elements. Very important after array buffer import.
What about a common interface for typed and untyped arrays? TypeableArray<T>
I have already published this type. You can take a look at the project page: typeable-array.
@saschanaz does not work. It lacks most functionality that a typed array would have such as length or even member access. The reason is that this interface is meant to be used for any view of arraybuffer. So DataView is also included.
I seriously don't see a problem with introducing an interface called TypedArray
.
As of the specifications (e.g. for Float64Array "All Float64Array
objects inherit from %TypedArray%.prototype
."), all (aforementioned) numerically typed arrays derive from the Prototype
of the TypedArray
class. Meaning: They share the same public TypedArray
properties and methods, hence they share the same public interface.
To enable a TypeScript-native (unhacky) abstraction for numerically typed arrays, I strongly recommend the introduction of a TypedArray
interface. This would be sufficient enough to handle the aforementioned issues and would obviously comply to the Specifications and finally ensure much less and cleaner code.
@dschnelldavis new
doesn't work because it's essentially an abstract constructor, for lack of a better terminology. However, instanceof
_does_ work as demonstrated in this snippet:
const TypedArray = Object.getPrototypeOf(Uint8Array)
console.log(new Uint8Array() instanceof TypedArray)
As other people have said, even though it's an invisible _constructor_, there should still be an _interface_ for it, as it encapsulates a lot of functionality in a single base class and drastically reduces repetition for definition files, as well as more accurately reflecting the specification.
I think there should also be an interface for TypedArrayConstructor
as well, since there are common static properties, so something like this:
interface TypedArray {
...
}
interface TypedArrayConstructor {
readonly prototype: TypedArray;
...
}
interface Float32Array extends TypedArray { }
interface Float64Array extends TypedArray { }
interface Int16Array extends TypedArray { }
interface Int32Array extends TypedArray { }
interface Int8Array extends TypedArray { }
interface Uint16Array extends TypedArray { }
interface Uint32Array extends TypedArray { }
interface Uint8Array extends TypedArray { }
interface Uint8ClampedArray extends TypedArray { }
interface Float32ArrayConstructor extends TypedArrayConstructor { }
interface Float64ArrayConstructor extends TypedArrayConstructor { }
interface Int16ArrayConstructor extends TypedArrayConstructor { }
interface Int32ArrayConstructor extends TypedArrayConstructor { }
interface Int8ArrayConstructor extends TypedArrayConstructor { }
interface Uint16ArrayConstructor extends TypedArrayConstructor { }
interface Uint32ArrayConstructor extends TypedArrayConstructor { }
interface Uint8ArrayConstructor extends TypedArrayConstructor { }
interface Uint8ClampedArrayConstructor extends TypedArrayConstructor { }
Has there been progress along this avenue?
Another example - node has fs.writeFile which accepts data
prop which can be TypedArray
. However, in @types/node
types data
is defined as any
. Same with many other libraries using fs
inside.
It would be really nice to have this one and change any
to proper types.
Most helpful comment
I seriously don't see a problem with introducing an interface called
TypedArray
.As of the specifications (e.g. for Float64Array "All
Float64Array
objects inherit from%TypedArray%.prototype
."), all (aforementioned) numerically typed arrays derive from thePrototype
of theTypedArray
class. Meaning: They share the same publicTypedArray
properties and methods, hence they share the same public interface.To enable a TypeScript-native (unhacky) abstraction for numerically typed arrays, I strongly recommend the introduction of a
TypedArray
interface. This would be sufficient enough to handle the aforementioned issues and would obviously comply to the Specifications and finally ensure much less and cleaner code.