这是includes 的一个已知问题,请参阅issues/26255。
但是,有一个解决方法。您可以创建自定义curriedtypeguard:
const inTuple = <Tuple extends string[]>(
tuple: readonly [...Tuple]) => (elem: string
): elem is Tuple[number] =>
tuple.includes(elem)
// (elem: string) => elem is "dog" | "cat"
const inPets = inTuple(Pets)
让我们试试吧:
const Pets = ["dog", "cat"] as const
type Pet = typeof Pets[number]
type Animal = Pet | "tiger"
const inTuple = <Tuple extends string[]>(
tuple: readonly [...Tuple]) => (elem: string
): elem is Tuple[number] =>
tuple.includes(elem)
// (elem: string) => elem is "dog" | "cat"
const inPets = inTuple(Pets)
function checkDanger(animal: Animal) {
if (inPets(animal)) {
animal // "dog" | "cat"
return "not dangerous"
}
return "very dangerous"
}
因为你有一个条件语句,我假设这个函数可以被重载以缩小返回类型:
const Pets = ["dog", "cat"] as const
type Pet = typeof Pets[number]
type Animal = Pet | "tiger"
const inTuple = <Tuple extends string[]>(
tuple: readonly [...Tuple]) => (elem: string
): elem is Tuple[number] =>
tuple.includes(elem)
// (elem: string) => elem is "dog" | "cat"
const inPets = inTuple(Pets)
function checkDanger(animal: Pet): "not dangerous"
function checkDanger(animal: Animal): "very dangerous"
function checkDanger(animal: string) {
if (inPets(animal)) {
animal // "dog" | "cat"
return "not dangerous"
}
return "very dangerous"
}
const result = checkDanger('tiger') // very dangerous
const result2 = checkDanger('cat') // not dangerous
Playground
重载签名的顺序很重要。
您可能已经注意到,我的代码中没有类型断言。
inTuple 类型保护有效,因为tuple 被视为函数体内的字符串数组。这意味着允许此操作,因为tuple[number] 和elem 可以相互分配。
const inTuple = <Tuple extends string[]>(
tuple: readonly [...Tuple]) =>
(elem: string): elem is Tuple[number] => {
tuple[2] = elem // ok
elem = tuple[3] // ok
return tuple.includes(elem)
}