TypeScript 中没有具体的类型可以代表您的 Declarations 形状。
我将一般概念称为“默认属性”类型。 (要求此问题的 GitHub 问题是 microsoft/TypeScript#17867)您希望特定属性为一种类型,然后将任何其他属性“默认”为其他不兼容的类型。这就像一个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>。这种自引用约束确保对于declarations 中的每个属性K,其类型均为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;
}>*/
好的,希望对您有所帮助;祝你好运!
Link to code