Typescript:至少有一个T类型属性的对象。

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Typescript:至少有一个T类型属性的对象。相关的知识,希望对你有一定的参考价值。

我怎么能写 HasNumber 没有排版稿的情况下给我一个错误,因为在 HasNumberAndString 名不副实 number? 我想要一个方法来执行 HasNumberAndString 至少有一个类型为 number (不强制执行属性名称),但也允许拥有其他类型的属性。这可能吗?

interface HasNumber {
    [key: string]: number;
}

interface HasNumberAndString extends HasNumber {
    age: number;
    name: string;
}
答案

在TypeScript中没有很好的方法来允许一个有索引签名的类型有一个不匹配索引签名类型的附加属性。


一个变通的方法是使用 交叉点 而不是扩展一个接口。

type SortaHasNumberAndString = { [key: string]: number } & { age: number, name: string };

你基本上是在试图愚弄编译器,因为从技术上讲,这就意味着 name 属性的类型都应该是 stringnumber,但你希望编译器只注意到了 string 部分。 当从该类型的值中读取属性时,这实际上是可行的。

declare const sortaHasNumberAndString: SortaHasNumberAndString;
sortaHasNumberAndString.name; // string, not number
sortaHasNumberAndString.age; // number
sortaHasNumberAndString.numberOfLimbs; // number due to index signature

但当你试图将其赋值给该类型的值时,就不太行得通了。

// error, complains that name is not string & number
const notSoGreat: SortaHasNumberAndString = {
  name: "Long John Silver",
  age: 43,
  numberOfLimbs: 3
}

所以你不得不使用断言或其他技巧。

const notSoGreat = {
  name: "Long John Silver",
  age: 43,
  numberOfLimbs: 3
} as any as SortaHasNumberAndString; // okay now

这就意味着你会在接受这种类型的函数时遇到麻烦:

declare function acceptSortaHasNumberAndString(x: SortaHasNumberAndString): void;
acceptSortaHasNumberAndString({name: "a", age: 123}); // error!

这并不理想


另一个解决方案(在我看来是更好的)是允许使用 HasNumberHasNumberAndString 将要 通用 中的数字属性。 这意味着没有索引签名需要担心。

type HasNumber<K extends keyof any> = Record<K, number>;
type HasNumberAndString<K extends keyof any> = { name: string } & HasNumber<Exclude<K, "name">>;

这使用了内置的 RecordExclude 类型的别名来说明 HasNumberAndString<K> 应有 name 类型的属性 string但所有其他属性应是类型 number. 如果你需要的话,你可以修改这些来反映不同的约束条件。 让我们看看它是如何工作的。

如果你想谈论一个特定类型的值 HasNumberAndString 你需要在类型中指定键。

const hns: HasNumberAndString<'age'> = {
  name: "Harry James Potter",
  age: 38
}

这可能不是很好,但在大多数情况下,你可以让TypeScript为你推断这些键。

const inferHasNumberAndString = 
  <T>(x: T & HasNumberAndString<keyof T>): HasNumberAndString<keyof T> => x;

const hns = inferHasNumberAndString({
  name: "Harry James Potter",
  age: 38
}); // inferred as type HasNumberAndString<"name" | "age">;

上面的辅助函数 inferHasNumberAndString 是通用的,它显示了你可以接受类型为 HasNumberAndString<K> 而不强制 K 自己。

declare function acceptHasNumberAndString<T>(x: T & HasNumberAndString<keyof T>): void;
acceptHasNumberAndString({ name: "Hermione Granger", age: 39 });

希望能帮到你,祝你好运!

另一答案

你可以用 联合类型 允许任何类型的属性 stringnumber:

interface HasNumber {
    [key: string]: number;
}

interface HasNumberAndString {
    [key: string]: string | number;
}

但这并不意味着每一种类型至少有一个属性。

以上是关于Typescript:至少有一个T类型属性的对象。的主要内容,如果未能解决你的问题,请参考以下文章

Java集合框架上机练习题:编写一个Book类,该类至少有name和price两个属性。该类要实现Comparable接口,在接口的compareTo()方法.....

TypeScript:推断嵌套联合类型的类型

Typescript 对象:如何将键限制为特定字符串?

TypeScript:在 keyof T 处获取属性的类型

Typescript杂谈

TypeScript 类型挑战 Medium