似乎将输入arg推导为通用索引签名并不能按预期工作(或者我完全缺少missing)。
如何使返回类型被推断并正确验证输入?
interface Styles {
contentAlign?: string;
zIndex?: number;
}
function createTheme<S extends { [key: string]: Styles }>(theme: S) {
return theme;
}
// this works, foo is marked as invalid
const style: Styles = {
zIndex: 1,
foo: 'bar', // <-- invalid
};
// once I try to use the Styles as index signature it allows other properties
const t = createTheme({
Button: {
zIndex: 1,
foo: 'bar', // <-- valid??
},
});
我期望Type { foo: "bar" } is not assignable to type Styles
,但这似乎是有效的输入
答案 0 :(得分:1)
S extends { [key: string]: Styles }
表示S
可以是{ [key: string]: Styles }
的子类型。但这也意味着S
的任何属性也可以是Styles
的子类型,因此这意味着任何给定的键实际上可以具有比Styles
中指定的属性更多的属性。
通常,在OOP中,允许在期望基本类型的地方分配子类型,Typescript仅在将对象文字直接分配给特定类型时才执行多余的属性检查。在分配给泛型类型参数时,编译器不会执行多余的属性检查,因为它假定您要允许子类型(毕竟S extends {...}
会读取扩展了S
的任何类型{...}
)。
在您的情况下,因为您想允许任何键,但是实际上您不想禁用Styles
上的多余属性检查,所以我将对象的键而不是整个对象的键用作类型参数:
interface Styles {
contentAlign?: string;
zIndex?: number;
}
function createTheme<K extends PropertyKey>(theme: Record<K, Styles>) {
return theme;
}
// this works, foo is marked as invalid
const style: Styles = {
zIndex: 1,
foo: 'bar', // <-- invalid
};
// once I try to use the Styles as index signature it allows other properties
const t = createTheme({
Button: {
zIndex: 1,
foo: 'bar', // <-- error
},
Header: {
zIndex: 1,
foo: 'bar', // <-- error
},
});