我正在编写一个API的包装器.根据传入的类型,API需要不同的参数.我希望这个不变量由类型脚本编译器强制执行,而不会引入任何不安全的行为,例如强制转换.
我在下面提供了一个我想要做的事情的例子.在这个类比中,eat
是API调用,Food
是可能的输入参数,Fruit
是我想要求调用者提供额外输入的参数的子集.MaybePeel
是一个"帮助器"类型,当传递Fruit
时,它将peel
属性添加到EatParams
类型.
我的方法有两个问题,可能是因为我缺乏打字知识.第一个是在方法上.当访问food.peel
时,我希望TypeScrip知道该属性必须存在.这是可行的,但问题是,TypeScrip允许我访问else
中的该属性,而该属性绝对不应该存在.第二个是在调用eat
方法时--TypeScrip要求Pizza
提供peel
.
type Apple = { name: "Apple" }
type Peach = { name: "Peach" }
type Pizza = { name: "Pizza" }
type Food = Apple | Peach | Pizza
type Fruit = Apple | Peach
// we should potentially peel fruit before eating, but we should never peel pizza (how would you even do that?)
type MaybePeel<T extends Food> = T extends Fruit ? {
peel: boolean;
} : never;
// For Pizza, we should just take a quantity parameter; never ask to peel
// For Fruit, we should take both quantity and a `peel` boolean
type EatParams<T extends Food> = {
food: T;
quantity: number;
} & MaybePeel<T>
function eat<T extends Food>(params: EatParams<T>) {
const { food } = params;
if (food.name === "Peach" || food.name === "Apple") {
// TypeScript should infer that `peel` must be set in this case because of `MaybePeel`
if (params.peel) {
// code to peel my food
}
} else {
// TypeScript should throw an error here because `Pizza` never takes a `peel` parameter
// @ts-expect-error
params.peel
}
// code to eat my food
}
// Pizza should not allow `peel` to be passed in
eat({ food: { name: "Pizza" }, quantity: 1 })
// Apple and Peach should require a `peel` to be passed in
eat({ food: { name: "Apple" }, quantity: 1, peel: true })