TypeScript如何收窄函数返回类型?优化Deque类类型推断
问题与解决方案
原代码与问题
type DequeNode<T> = { value: T; prev?: DequeNode<T>; next?: DequeNode<T>; }; class Deque<T> { head?: DequeNode<T>; tail?: DequeNode<T>; size = 0; first() { return this.head?.value; } isEmpty() { return this.head == null } } const deque = new Deque<number>() if (!deque.isEmpty()) { deque.first() // 类型为number | undefined(不符合预期) deque.head?.value // 类型为number | undefined(不符合预期) } if (deque.head) { deque.first() // 类型为number | undefined(不符合预期) deque.head.value // 类型为number(符合预期) }
在上述代码中:
- 直接检查
deque.head非空时,TypeScript能正确推断deque.head.value的类型,但deque.first()仍被推断为number | undefined。 - 通过
isEmpty()判断队列非空后,deque.first()和deque.head?.value的类型依然是number | undefined,无法自动缩小为number。
解决方法
要让TypeScript通过isEmpty()的检查自动缩小类型,需要结合类型谓词和方法重载来实现:
type DequeNode<T> = { value: T; prev?: DequeNode<T>; next?: DequeNode<T>; }; class Deque<T> { head?: DequeNode<T>; tail?: DequeNode<T>; size = 0; // 用类型谓词定义isEmpty,告诉TS:返回false时,当前实例的head一定非空 isEmpty(): this is { head: DequeNode<T>; tail?: DequeNode<T>; size: number } { return this.head == null; } // 给first方法添加重载:根据实例状态返回对应类型 first(): T | undefined; first(this: { head: DequeNode<T> }): T; first() { return this.head?.value; } } const deque = new Deque<number>(); if (!deque.isEmpty()) { deque.first(); // 类型推断为number(符合预期) deque.head.value; // 类型推断为number(符合预期) } if (deque.head) { deque.first(); // 类型推断为number(符合预期) deque.head.value; // 类型推断为number(符合预期) }
关键说明
- 类型谓词的作用:将
isEmpty()的返回值定义为this is { head: DequeNode<T>; ... },当isEmpty()返回false时,TypeScript会自动将当前Deque实例的类型缩小为head非空的版本。 - 方法重载的作用:给
first()添加重载签名,明确当this的head非空时,返回值为T;否则返回T | undefined。这样在通过类型守卫(isEmpty()或deque.head检查)后,TypeScript能正确推断first()的返回类型。
内容的提问来源于stack exchange,提问作者Marvin Xu
相关产品推荐
相关产品推荐

