我有一个函数,它接受一个参数,可以有两种不同类型之一。我想根据该参数键入返回类型。
我需要使用一个类型参数,这样我就可以在返回类型中引用参数类型。但我不想使用extends
关键字,因为这将允许将任何超类型放入函数中。示例:
type Cat = {
name: string;
}
type CatWithOwner = {
owner: string;
} & Cat;
const getNicknamedCat = <C extends Cat | CatWithOwner>(
cat: C
): C extends CatWithOwner
? CatWithOwner & { nickname: string }
: Cat & { nickname: string } => {
return { ...cat, nickname: name + "y" };
};
// works
getNicknamedCat({ name: "Nick" })
// works, return type also has the owner
getNicknamedCat({ name: "Nick", owner: "Winston" })
// works, too, but I don't want this. I want to forbid non-Cat/CatWithOwner-keys
getNicknamedCat({ name: "Nick", unrelated: "foo" })
我需要类型参数,这样我就可以在返回类型中引用它,但是有没有什么方法可以“分配”类型参数一个类型而不是扩展?
我想要的是这样的:
const getNicknamedCat = <C is Cat | CatWithOwner>(
cat: C
): C extends CatWithOwner
? CatWithOwner & { nickname: string }
: Cat & { nickname: string } => {
return { ...cat, nickname: name + "y" };
};
// works
getNicknamedCat({ name: "Nick" })
// works, return type also has the owner
getNicknamedCat({ name: "Nick", owner: "Winston" })
// error, "unrelated" is not expected
getNicknamedCat({ name: "Nick", unrelated: "foo" })
1条答案
按热度按时间von4xj4u1#
Typescript使用结构化类型系统,也称为“duck-typing”,它检查类型的形状,与Java等静态类型语言相比,前者检查身份。这意味着您可以预期
X
类型,但能够发送具有相同形状和一些额外属性的Y
类型。幸运的是,有一个解决方案。不是理想的,但至少它工作。
逻辑:由于你想检查传递的类型是否是联合体的成员(
Cat
或CatWithOwner
),我们需要使用分布条件类型来分别处理联合体的每个成员。之后,我们将从选中的类型中删除成员联合的所有键。如果在此之后它将是一个空对象,这意味着没有额外的字段。然而,它不能递归地工作,因此如果你有嵌套的额外字段,它们将通过检查。但是,这种类型可以很容易地适应。如果该类型不是空对象,我们可以返回never
,这将触发一个错误或一点复杂的错误消息,让用户知道什么是错误的。后一个可以通过添加一些symbol
属性来完成,该属性将包含额外字段的名称:用途:
测试:
链接到Playground