假设我有一个扩展在内置MyArray<T>类上的自定义Array<T>类,我应该如何键入,以便myMap<T>(myArr: MyArray<T>, <Function>)能够正确地推断返回类型为MyArray<T>,而不是内置Array<T>
PS:我希望这个类型声明尽可能通用,这就是为什么我没有简单地重载map方法的原因。这样,我可以很容易地将myArr中的类型变量Iterable<T>的签名更改为Iterable<T>,这也可以用于实现Iterable的其他内置/自定义类,比如Set<T>。现在,我能做的最好的就是让用户将他们想要的返回类型指定为一个泛型函数变量。
class MyArray<T> extends Array<T> { }
declare const myArr: MyArray<number>;
const myArrMapped_ = myArr.map(x => x); // expected MyArray<number>, got number[]
// Which is understandable, since `Array.prototype.map`'s type signiture
// has no way to determine what is returned by `MyArray<T>[Symbol.species]`.
// However, if I were to implement my own `Array.prototype.map`, how can I
// obtain the typing of `MyArray<T>[Symbol.species]` so that it correctly
// returns MyArray<number> in the above example?
import { Expect, Equal } from '@type-challenges/utils';
declare function myMap<T, Arr extends T[], R>(arr: Arr, func: any): any; // myMap
const myArrMapped = myMap(myArr, (x: number) => x);
type test = Expect<Equal<typeof myArrMapped, MyArray<number>>> // how can I make this work?
// what I tried to get `MyArray[Symbol.species]`
type HasSpecies<Species> = {
[Symbol.species]: Species;
}
// @ts-expect-error: apparently ts yells at me for doing this
class MyArray1<T> extends Array<T> {
static get [Symbol.species]() { return MyArray1 }
}
const species1 = MyArray[Symbol.species];
type didnt_work = [
MyArray<any>[Symbol.species], // 'Symbol' only refers to a type, but is being used as a namespace here.
MyArray<any>[Symbol['species']], // 'Property 'species' does not exist on type 'Symbol'.
MyArray<any>[typeof Symbol.species], // Type 'unique symbol' cannot be used as an index type.
MyArray<any> extends HasSpecies<infer Species> ? Species : never, // never
typeof (MyArray[Symbol.species]), // just doesn't work
typeof species1, // apparently returns ArrayConstructor instead, and I must extract type from a variable
]
class MyArray2<T> extends Array<T> {
static get species() { return MyArray1 }
}
type a = typeof Array[Symbol.species];
const species2 = MyArray1[Symbol.species];
type worked_but_not_the_way_i_wanted = [
typeof MyArray2.species, // not Symbol.species,
typeof species2, // kindof worked, so far the best solution, but I don't think
// I'm able to get here inside the `myMap` function, and I have
// to suppress a ts error.
]
// suggestions:
// 1. allow symbol as index key after the typeof operator
//
// 2. there could be a `prototypeof` operator or a intrinsic `PrototypeOf<T>` type
//
// that way I could declare function `myMap` as this:
// function myMap<T, Arr extends T[], R>(arr: Arr, func: (x: T) => R): ReturnType<(prototypeof Arr)[Symbol['species']]>;
// or this
// function myMap<T, Arr extends T[], R>(arr: Arr, func: (x: T) => R): ReturnType<(PrototypeOf<Arr>)[Symbol['species']]>;发布于 2021-06-03 11:23:00
我相信您不必指定任何static属性,因为您必须全部实现它们。
由于您希望覆盖一些Array.prototype方法,所以我认为最好只键入您感兴趣的那些方法。
import { Expect, Equal } from '@type-challenges/utils';
class MyArray<T> extends Array<T> {
override map<U>(callbackfn: (value: T, index: number, array: MyArray<T>) => U, thisArg?: any): MyArray<U> {
return []
}
}
const instance = new MyArray<number>()
const result = instance.map((elem) => elem * 2)
type test = Expect<Equal<typeof result, MyArray<number>>> // ok问题是Symbol.species是静态的
因此,无法从实例中访问它。请参阅源代码
考虑下一个例子:
type Keys = {
[P in keyof SetConstructor]:P
}
// type Keys = {
// readonly prototype: "prototype";
// readonly [Symbol.species]: typeof Symbol.species;
// }和:
type Keys = {
[P in keyof Set<number>]:P
}
// type Keys = {
// add: "add";
// clear: "clear";
// delete: "delete";
// forEach: "forEach";
// has: "has";
// readonly size: "size";
// entries: "entries";
// keys: "keys";
// values: "values";
// [Symbol.iterator]: typeof Symbol.iterator;
// readonly [Symbol.toStringTag]: typeof Symbol.toStringTag;
// }因此,Symbol.species只能从constructor中推断。这对练习意味着什么?
const NOT_IMPLEMENTED = null as any;
type ClassType = new (...args: any[]) => any
// if Symbol.species] exists in T - it is a COnstructor
type Species<T> = T extends {
readonly [Symbol.species]: infer Constructor;
} ? Constructor : never
interface MyArray<T> {
[Symbol.iterator](): IterableIterator<T>;
}
interface MyArrayConstructor {
new(): any
[Symbol.species]: MyArrayConstructor
}
declare var MyArray: MyArrayConstructor;
const builder = <T extends ClassType>(constructor_: T): Species<T> => NOT_IMPLEMENTED
const set = builder(Set) // SetConstructor
const array = builder(Array) // ArrayConstructor
const myArray = builder(MyArray) // MyArrayConstructor
type Test1 = Species<typeof MyArray> // MyArrayConstructor
type Test2 = Species<typeof Set> // SetConstructor
type Test3 = Species<typeof Map> // MapConstructor参见此示例:
type GetPrototype<T> = T extends { prototype: infer Proto } ? Proto : never
const NOT_IMPLEMENTED = null as any;
type Species<T> = T extends {
readonly [Symbol.species]: infer Constructor;
} ? Constructor : never
type IteratorElement<T extends Iterable<any>> = T extends {
[Symbol.iterator](): Iterator<infer Elem>
} ? Elem : never
type Instance = InstanceType<SetConstructor> // Set<unknown>
function map<Elem extends Iterable<any>, Constructor extends Species<Elem> >(array: Elem, cb: (elem: IteratorElement<Elem>) => any): GetPrototype<Elem> {
return NOT_IMPLEMENTED;
}
// GetPrototype<Elem> === never
// Constructor
const result = map(new Set<number>(), elem => elem + 2) 当您只有实例时,可以推断构造函数的实例,但反之亦然。
https://stackoverflow.com/questions/67815551
复制相似问题