我正在将我的小库从JavaScript转换为TypeScript,并且在那里有一个函数
function create(declarations: Declarations) {
现在,声明是一个对象,其键可以为两种类型:
onMemeber
/ onCollection
,则值应为数字可以使用TypeScript强制执行吗?我应该如何定义我的Declarations
接口?
答案 0 :(得分:1)
这似乎是不可能的,in the TypeScript Deep Dive book上有专门的章节。尽管可以声明类型,但实际上无法在TS中使用它创建对象:
// No error on this type declaration:
type Declarations = {
[key: string]: string;
} & {
onMember: number;
onCollection: number;
}
// Error does appear here indicating type `number` is not assignable to type `string`.
const declarations: Declarations = {
onMember: 0,
onCollection: 0,
other: 'Is a string'
}
答案 1 :(得分:1)
TypeScript中没有具体的类型可以表示您的Declarations
形状。
我将一般概念称为“默认属性”类型。 (要求这是microsoft/TypeScript#17867的GitHub问题)您希望特定属性属于一种类型,然后让其他属性“默认”为其他一些不兼容的类型。就像index signature,没有所有属性都必须可分配给它的约束。
(请注意,不能使用索引签名:
type BadDeclarations = {
onMember: number, // error! number not assignable to string
onCollection: number, // error! number not assignable to string
[k: string]: string
};
索引签名[k: string]: string
表示每个属性必须可分配给string
,甚至onMember
和onCollection
。要使索引签名真正起作用,您需要将属性类型从string
扩展到string | number
,这可能对您不起作用。 )
有一些pull requests that would have made this possible,但看起来它们很快不会成为语言的一部分。
通常在TypeScript中,如果没有适用的具体类型,则可以以某种方式使用generic type,即constrained。这是使Declarations
通用的方法:
type Declarations<T> = {
[K in keyof T]: K extends 'onMember' | 'onCollection' ? number : string
};
这是create()
L
function create<T extends Declarations<T>>(declarations: T) {
}
您可以看到declarations
参数的类型为T
,它被约束为Declarations<T>
。此自引用约束确保对于K
的每个属性declarations
,它的类型为K extends 'onMember' | 'onCollection' ? number : string
,是conditional type的类型,可以很容易地转换出所需形状。
让我们看看它是否有效:
create({
onCollection: 1,
onMember: 2,
randomOtherThing: "hey"
}); // okay
create({
onCollection: "oops", // error, string is not assignable to number
onMember: 2,
otherKey: "hey",
somethingBad: 123, // error! number is not assignable to string
})
在我看来,这很合理。
当然,使用泛型类型并非没有烦恼。突然之间,您想要与Declarations
一起使用的每个值或函数现在都必须是通用的。因此,您无法执行const foo: Declarations = {...}
。您需要使用const foo: Declarations<{onCollection: number, foo: string}> = {onCollection: 1, foo: ""}
来代替。令人讨厌的是,您可能想要使用一个辅助函数,例如允许为您推断此类类型,而不是手动添加注释:
// helper function
const asDeclarations = <T extends Declarations<T>>(d: T): Declarations<T> => d;
const foo = asDeclarations({ onCollection: 1, foo: "a" });
/* const foo: Declarations<{
onCollection: number;
foo: string;
}>*/
好的,希望能有所帮助;祝你好运!