在 TypeScript 中,你通常会使用类型守卫(Type Guards)来在函数体内判断参数的类型。类型守卫是一种表达式,它执行运行时检查并返回一个类型信息给 TypeScript 的类型系统。
你可以通过自定义类型守卫函数或者使用 in
关键字配合类型断言来实现这一点。但是,请注意,TypeScript 的类型系统只在编译时存在,运行时并不包含类型信息,所以类型守卫实际上是执行一些运行时的检查来模拟类型信息。
下面是一个使用 in
关键字和类型断言的示例:
interface Person {
name: string;
age: number;
}
interface Animal {
food: string;
kind: string;
}
function isPerson(some: any): some is Person {
return 'name' in some && 'age' in some;
}
function isAnimal(some: any): some is Animal {
return 'food' in some && 'kind' in some;
}
function test(some: Person | Animal) {
if (isPerson(some)) {
// 在这个分支中,TypeScript 知道 some 是 Person 类型
console.log(some.name, some.age);
} else if (isAnimal(some)) {
// 在这个分支中,TypeScript 知道 some 是 Animal 类型
console.log(some.food, some.kind);
}
}
// 示例使用
const personObj: Person = { name: 'Alice', age: 30 };
const animalObj: Animal = { food: 'meat', kind: 'dog' };
test(personObj); // 输出 Alice 30
test(animalObj); // 输出 meat dog
在这个例子中,isPerson
和 isAnimal
函数是自定义的类型守卫。它们接收一个 any
类型的参数,并返回一个布尔值,表示参数是否符合 Person
或 Animal
类型。通过返回 some is Person
或 some is Animal
,TypeScript 的类型系统会理解在这些函数内部的条件分支中,some
的确切类型是什么。
这样,你就可以在 test
函数内部安全地使用 some
对象的特定属性,而不用担心类型错误了。
话先说在头,其实TS官网教程类型收窄这章里,讲到
typeof
检查、in
检查,已经能做到大部分情况的类型收窄了,一般业务开发没必要过于苛求运行时的检查。像题主的例子里,if ('name' in some) {}
和if ('food' in some) {}
就能自动收窄Person
和Animal
了。除非你对类型检查有很极致的追求。以下正文。
interface
和type
属于编译期行为,编译结束后是不存在的,所以你在JS里是无法直接使用interface
来判断类型。解决办法,一种是写谓词函数(predicate)来手动检查。这种函数的返回值是
value is SomeType
形式。看代码你也发现,这样手写
predicate
非常繁琐,而且容易有遗漏。所以有一个工具库io-ts可以代替繁琐的工作。
在TS里,
class
既是type也是value,所以,还有一种方法,就是干脆用class
创建对象,然后用instanceof
检查原型链判断类型这就是很经典的
OOP
开发方式。如果此前习惯传统JS匿名对象的写法,可能需要一定时间才能适应。