如何在 Typescript 泛型中获取数组类型

And*_*eas 6 typescript

我有一个这样的方法:

public select(fieldName: keyof TType)
Run Code Online (Sandbox Code Playgroud)

哪里TType可以是数组类型。在数组类型的情况下,fieldName将正确地为我提供 type 的所有属性名称Array

如果我使用一种类型调用此方法,User[]我想获取 的属性User而不是 的属性Array

有没有办法做到这一点?

额外问题:有没有办法限制 TType 为数组类型?

jca*_*alz 18

您绝对可以制作一个条件类型函数,将数组类型解包至一层深度,然后keyof在其结果上使用。例如:

// unwrap up to one level
type Unarray<T> = T extends Array<infer U> ? U : T;

// your class maybe
declare class Thingy<T> {
  constructor(t: T);
  public select(fieldName: keyof Unarray<T>): void;
}
// your interface maybe
interface User {
  name: string,
  age: number
}

declare const u1: User;
declare const u2: User;
const x = new Thingy(u1);
x.select("name"); // okay
const y = new Thingy([u1, u2]);
y.select("age"); // okay
y.select("push"); // error
Run Code Online (Sandbox Code Playgroud)

对于打字,我认为这应该可以正常工作。显然,您还需要有一个有效的实现(并注意,实现中的条件类型通常需要一些类型断言或重载才能使编译器满意……但您似乎问的是类型,而不是实现)。


至于您的额外问题,是的,您可以限制T为仅数组类型,如下所示:

// your class maybe
declare class Thingy<T extends Array<any>> {
  constructor(t: T);
  public select(fieldName: keyof (T[number])): void;
}
// your interface maybe
interface User {
  name: string,
  age: number
}

declare const u1: User;
declare const u2: User;
const x = new Thingy(u1); // error
const y = new Thingy([u1, u2]);
y.select("age"); // okay
Run Code Online (Sandbox Code Playgroud)

请注意,我在这里完全取消了条件类型,因为它更直接。


希望有所帮助;祝你好运!


Kar*_*ski 10

您将需要一个小帮手来提取盒装类型:

type Unboxed<T> =
    T extends (infer U)[]
        ? U
        : T;
Run Code Online (Sandbox Code Playgroud)

那么你的方法可以如下所示:

interface User {
    id: symbol;
    name: string;
}

class Foo {
    select(fieldName: keyof Unboxed<User[]>) {
        console.log(fieldName) // "id" | "name"
    }
}
Run Code Online (Sandbox Code Playgroud)

至于你的额外问题:是的,有可能,但可能感觉有点奇怪。

class Foo {
    select<T extends any[]>(fieldName: keyof Unboxed<T>) {
        console.log(fieldName)
    }
}

new Foo()
  .select<Window[]>('addEventListener')
Run Code Online (Sandbox Code Playgroud)

类型参数旨在描述方法内部的参数或类的泛型类型。所以也许您想要执行以下操作:

class Foo<T extends any[]> {
    select(fieldName: keyof Unboxed<T>) {
        console.log(fieldName)
    }
}

new Foo<Window[]>()
  .select('addEventListener')
Run Code Online (Sandbox Code Playgroud)