我注意到一些奇怪的行为:
在普通情况下,如果对象包含太多键,TypeScript会抱怨,比如:
type Foo = {
a: string;
}
const a: Foo = {
a: "hello",
b: "foo" // Object literal may only specify known properties, and 'b' does not exist in type 'Foo'.(2322)
};
function returnsFoo() : Foo {
return {
a: 'hello',
b: "world" // Object literal may only specify known properties, and 'b' does not exist in type 'Foo'.(2322)
}
}
然而,如果我不是直接声明一个函数,而是声明一个具有函数作为属性的类型,那么这个错误就会消失:
type ObjectThatHasAFunctionThatReturnsFoo = {
defaultData: () => Foo;
};
const b: ObjectThatHasAFunctionThatReturnsFoo = {
defaultData: ()=> ({
a: "hello",
//@ts-expect-error - but no error.
b: "asa"
})
}
为什么会这样?
请查看文档
对象文字在将其分配给其他变量或将其作为参数传递时会受到特殊处理,并进行过多的属性检查。如果一个对象文字有任何"目标类型"没有的属性,你会得到一个错误:
绕过这些检查的最后一种方法是将对象分配给另一个变量,这可能有点令人惊讶:由于squareOptions不会进行过多的属性检查,因此编译器不会给您错误。
我相信你在这里没有错误:
type Foo = {
a: string;
}
const a: Foo = {
a: "hello",
b: "foo" // Object literal may only specify known properties, and 'b' does not exist in type 'Foo'.(2322)
};
function returnsFoo(): Foo {
return {
a: 'hello',
b: "world" // Object literal may only specify known properties, and 'b' does not exist in type 'Foo'.(2322)
}
}
type ObjectThatHasAFunctionThatReturnsFoo = {
defaultData: () => Foo;
};
const b: ObjectThatHasAFunctionThatReturnsFoo = {
defaultData: () => ({
a: "hello",
b: "foo"
})
}
游乐场
因为函数CCD_ 1事实上被分配给CCD_。
因为您既不将对象文字分配给其他变量,也不将其用作参数——所以没有多余的属性检查
这是正确的-额外的b属性意味着开发人员犯了一个错误
为了做到这一点,您可能需要进行一些验证:
type Foo = {
a: string;
}
type Valid = {
defaultData: () => Foo;
};
type Validation<T> =
T extends { defaultData: infer Fn }
? Fn extends (...args: any[]) => any
? ReturnType<Fn> extends Foo
? Foo extends ReturnType<Fn>
? T
: never
: never
: never
: never
const builder = <T extends Valid>(arg: Validation<T>) => arg
builder({
defaultData: () => ({ // expected error
a: "hello",
b: "foo"
})
})
builder({
defaultData: () => ({ // ok
a: "hello",
})
})
在这里,在我的博客中,你可以找到更多关于通用验证的信息