有没有一种方法可以“提取”TypeScript接口属性的类型?

337

假设有一个针对库X的打字文件,其中包括一些接口。

interface I1 {
    x: any;
}
    
interface I2 {
    y: {
        a: I1,
        b: I1,
        c: I1
    }
    z: any
}

为了使用这个库,我需要传递一个对象,该对象与I2.y完全相同的类型。当然,我可以在我的源文件中创建完全相同的接口:

interface MyInterface {
    a: I1,
    b: I1,
    c: I1
}

let myVar: MyInterface;

但是我需要更新它,使其与库中的代码保持同步,而且它可能非常大,导致很多代码重复。是否有办法“提取”接口属性的类型?类似于let myVar: typeof I2.y(这不起作用,会导致“找不到名称I2”错误)。


编辑:在TS Playground中尝试后,我发现以下代码正好可以实现我的要求:

declare var x: I2;
let y: typeof x.y;

然而这需要声明一个冗余变量x。我正在寻找一种不需要这个声明就能实现的方法。


1
“which doesn't work” - 这是什么表现形式?你看到的实际错误信息是什么? - Bartek Banachewicz
@BartekBanachewicz 已更新 - Kuba Jagoda
6个回答

598

以前是不可能的,但幸运的是现在可以了,自从TypeScript版本2.1发布以来。它于2016年12月7日发布,并引入了索引访问类型,也称为查找类型

语法看起来像元素访问,但是写在类型的位置上。所以在你的情况下:

interface I1 {
    x: any;
}

interface I2 {
    y: {
        a: I1,
        b: I1,
        c: I1
    }
    z: any
}

let myVar: I2['y'];  // indexed access type

现在myVar的类型是I2.y
TypeScript Playground中查看一下。

4
如果 'y' 是一个数组,在不改变意思的情况下,有没有办法提取元素的类型?例如:I2{ y: {..}[]}。 - John B
2
@JohnB 是的,你可以用完全相同的方式来做,因为数组索引就像对象属性一样。在这里查看:http://www.typescriptlang.org/play/#src=let%20x%3A%20Array%3Cnumber%3E%20%3D%20%5B%5D%3B%0D%0Alet%20y%3A%20typeof%20x%5B0%5D%3B%20%2F%2Fy%20is%20of%20type%20%22number%22%0D%0A%0D%0A%2F%2F也适用于元组%0D%0Alet%20x1%3A%20%5Bstring%2C%20boolean%2C%20number%5D%3B%0D%0Alet%20y1%3A%20typeof%20x1%5B1%5D%3B%20%2F%2Fy1%20is%20of%20type%20boolean - Kuba Jagoda
3
这真是一项非常棒的能力。 - Geradlus_RU
5
假设我们正在循环遍历使用I2作为类型定义的对象的键。当循环时,如何动态获取特定键的类型?这个语句let z: typeof x[a];中,a是一个字符串类型的某个键,但它并不起作用。它告诉我a是指一个值而不是一个类型。我该如何解决这个问题?是否有任何方法可以做到?谢谢! - Emil Walser
显示剩余5条评论

35

扩展已接受的答案,您还可以使用 type 关键字分配类型并在其他地方使用它。

// Some obscure library
interface A {
  prop: {
    name: string;
    age: number;
  }
}

// Your helper type
type A_Prop = A['prop']

// Usage
const myThing: A_prop = { name: 'June', age: 29 };

22

这只是从联合对象类型中提取文字类型的一个例子:

type Config = {
    key: "start_time",
    value: number,
} | {
    key: "currency",
    value: string,
}

export type ConfigKey = Config["key"];
// "start_time"|"currency"


这就是我在寻找的答案。不知道为什么它没有被点赞。 无论如何,谢谢! - Sergey Potapov

17

Colorskeyof 会返回一个所有键为 "white" | "black" 的列表。当这个键列表被传递给 Colors 接口时,类型将是给定键的所有值,"#fff" | #000

interface Colors {
  white: "#fff"
  black: "#000"
}

type ColorValues = Colors[keyof Colors]
// ColorValues = "#fff" | "#000"

-1
const foo = ()=>{
   return {name: "test", age: 5}
}
type T1 = ReturnType<typeof foo> // {name: string, age: number}
type T2 = ReturnType<typeof foo>['name'] // string
type T3 = T1['age'] // number

-5

接口就像一个对象的定义。那么y是你的I2对象的一个属性,它是某种类型,例如“匿名”。

你可以使用另一个接口来定义y,并将其用作你的y类型,如下所示

interface ytype {
   a: I1;
   b: I1;
   c: I1;
}

interface I2 {
    y: ytype;
    z: any;
}

你可以将你的接口放在一个文件中,并使用 extract 命令,这样你就可以在项目的其他文件中导入它。

export interface ytype {
   a: I1;
   b: I1;
   c: I1;
}



 export interface I2 {
        y: ytype;
        z: any;
    }

你可以这样导入它:
   import {I1, I2, ytype} from 'your_file'

2
一切都很好,但正如我所提到的 - 接口I1和I2来自外部库,并在该库的d.ts文件中定义。因此,拥有这个ytype接口将会是代码重复,并且需要不断更新。 - Kuba Jagoda

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接