首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >类型标Symbol.species分型推理

类型标Symbol.species分型推理
EN

Stack Overflow用户
提问于 2021-06-03 04:54:00
回答 1查看 232关注 0票数 0

假设我有一个扩展在内置MyArray<T>类上的自定义Array<T>类,我应该如何键入,以便myMap<T>(myArr: MyArray<T>, <Function>)能够正确地推断返回类型为MyArray<T>,而不是内置Array<T>

PS:我希望这个类型声明尽可能通用,这就是为什么我没有简单地重载map方法的原因。这样,我可以很容易地将myArr中的类型变量Iterable<T>的签名更改为Iterable<T>,这也可以用于实现Iterable的其他内置/自定义类,比如Set<T>。现在,我能做的最好的就是让用户将他们想要的返回类型指定为一个泛型函数变量。

代码语言:javascript
复制
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']]>;

操场连接

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2021-06-03 11:23:00

我相信您不必指定任何static属性,因为您必须全部实现它们。

由于您希望覆盖一些Array.prototype方法,所以我认为最好只键入您感兴趣的那些方法。

代码语言:javascript
复制
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是静态的

因此,无法从实例中访问它。请参阅源代码

考虑下一个例子:

代码语言:javascript
复制
type Keys = {
    [P in keyof SetConstructor]:P
}
// type Keys = {
//     readonly prototype: "prototype";
//     readonly [Symbol.species]: typeof Symbol.species;
// }

和:

代码语言:javascript
复制
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中推断。这对练习意味着什么?

代码语言:javascript
复制
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

参见此示例:

代码语言:javascript
复制
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) 

当您只有实例时,可以推断构造函数的实例,但反之亦然。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/67815551

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档