打字稿:对象至少具有T类型的属性

时间:2018-10-30 02:23:00

标签: typescript typescript-typings

HasNumber上的名字不是HasNumberAndString的情况下,我怎么写number却没有打字稿给我一个错误?我想要一种方法来强制HasNumberAndString拥有至少一个类型为number的属性(而不强制执行属性名称),但也允许其具有其他类型的属性。这可能吗?

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

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

2 个答案:

答案 0 :(得分:0)

您可以使用union types来允许类型为stringnumber的任何属性:

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

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

但这不会强制每种类型至少具有一个属性。

答案 1 :(得分:0)

在TypeScript中,没有什么好方法允许带有索引签名的类型具有与索引签名类型不匹配的其他属性。


一种解决方法是使用type intersection而不是扩展接口:

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成为具有数字属性的键中的generic。这意味着无需担心索引签名。

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的{​​{1}}属性,但所有其他属性属性应为string类型。您可以根据需要修改这些设置以反映不同的约束。让我们看看它是如何工作的:

如果要讨论类型number的特定值,则需要在该类型中指定键:

HasNumberAndString

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

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

上述辅助函数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

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