为何仅联合类型报错,单一类型无错误?
问题描述
为简化复杂输入的函数签名,我们会定义一组关联令牌的「已知」复杂输入,允许用户传入令牌替代。以下是处理三元组的函数:
初始版本无编译错误:
const TUPLE_LENGTH = 3 type Tuple = readonly [unknown, unknown, unknown] function fn(tuple: Tuple): void { if (tuple.length !== TUPLE_LENGTH) { throw new Error(`Expected ${TUPLE_LENGTH} items, instead got ${tuple.length}`) } }
添加「已知」输入后,出现编译错误:
const TUPLE_LENGTH = 3 type Tuple = readonly [unknown, unknown, unknown] const knownTuples = { foo: [1, 2, 3], bar: ['a', 'b', 'c'], baz: [true, false, null], } satisfies Readonly<Record<string, Tuple>> type TupleName = keyof typeof knownTuples function fn(tuple: Tuple | TupleName): void { if (typeof tuple === 'string') { tuple = knownTuples[tuple] } if (tuple.length !== TUPLE_LENGTH) { throw new Error(`Expected ${TUPLE_LENGTH} items, instead got ${tuple.length}`) // ^^^^^^ // Error: Property 'length' does not exist on type 'never' } }
疑问:为何第一种情况中if (tuple.length !== TUPLE_LENGTH)后tuple仍为Tuple类型,第二种却被收窄为never类型?
原因分析
第一种情况(仅接受Tuple类型)
Tuple是固定长度的只读三元组类型,TypeScript在编译时明确它的length类型是字面量3,但依然允许编写if (tuple.length !== TUPLE_LENGTH)的运行时检查——因为TypeScript无法阻止你通过类型断言、any等方式传入不符合Tuple类型的值(比如长度不为3的数组)。因此,TypeScript不会把if块内的tuple收窄为never,你依然可以访问tuple.length。
第二种情况(接受Tuple | TupleName类型)
knownTuples中的每个值被TypeScript自动推断为更具体的字面量元组类型(比如foo对应readonly [1,2,3]),而非宽泛的Tuple类型。这些具体元组的length属性都是字面量3。- 在
if (typeof tuple === 'string')分支中赋值后,tuple的类型收窄为Tuple | 所有具体字面量元组的联合类型。 - 这个联合类型的所有成员,
length属性都是字面量3,而TUPLE_LENGTH是值为3的常量,类型同样是字面量3。TypeScript判定tuple.length !== TUPLE_LENGTH这个条件永远不可能成立——没有任何符合联合类型的值会满足该条件。 - 因此,
if块内部的tuple被收窄为never类型(不存在任何可能的取值),自然无法访问never类型的length属性,从而抛出编译错误。
内容的提问来源于stack exchange,提问作者Parzh
相关产品推荐
相关产品推荐

