如何在TypeScript中实现两种类型的编译期相等验证?
TypeScript编译期类型断言(类似C语言
static_assert) 我想实现类似C语言static_assert()的功能,仅针对编译期数据(比如不会被输出的类型信息),期望效果如下:
type TStrLit_0 = 'a' | 'b' | 'C' | 'd'; type TStrLit_1 = 'a' | 'b' | 'C' | 'd'; type TStrLit_2 = 'a' | 'b' | 'c' | 'd'; type TStrLit_3 = 'a' | 'b' | 'C'; type TStrLit_4 = 'a' | 'b' | 'C' | 'd' | 'e'; static_assert(TypesAreEqual<TStrLit_0, TStrLit_1>()); static_assert(!TypesAreEqual<TStrLit_0, TStrLit_2>()); static_assert(!TypesAreEqual<TStrLit_0, TStrLit_3>()); static_assert(!TypesAreEqual<TStrLit_0, TStrLit_4>()); type TObject_0 = { a: number, b: string }; type TObject_1 = { a: number, b: string }; type TObject_2 = { a: number, b: number }; type TObject_3 = { a: number, B: string }; type TObject_4 = { a: number }; type TObject_5 = { a: number, b: string, c: number }; type TObject_6 = { a: any, b: any }; static_assert(TypesAreEqual<TObject_0, TObject_1>()); static_assert(!TypesAreEqual<TObject_0, TObject_2>()); static_assert(!TypesAreEqual<TObject_0, TObject_3>()); static_assert(!TypesAreEqual<TObject_0, TObject_4>()); static_assert(!TypesAreEqual<TObject_0, TObject_5>()); static_assert(!TypesAreEqual<TObject_0, TObject_6>());
我需要基于TypeScript类型信息的通用编译期断言能力。编译器已经生成了类型信息但不会输出,运行时断言需要额外嵌入元数据,操作繁琐。编译期类型断言可以把假设的不变量嵌入代码,避免后续维护时无意破坏。
现在有两个问题:
- TypeScript是否已经内置了这类功能?
- 这个功能需要修改TypeScript核心,还是可以通过现有语法/功能实现?
解答
1. 是否已有内置功能?
目前TypeScript没有提供和C语言static_assert完全一致的原生语法,但从v4.9版本开始的satisfies运算符,以及类型系统的条件报错机制,可以间接实现类似效果,社区也有成熟的模拟方案。
2. 能否通过现有功能实现?
不需要修改TypeScript核心,完全可以通过现有类型系统特性实现。核心思路是利用条件类型触发编译报错:当断言条件不满足时,让TypeScript抛出类型错误。
具体实现
首先实现严格的类型相等判断工具:
// 严格判断两个类型是否完全相等 type TypesAreEqual<T, U> = (<G>() => G extends T ? 1 : 2) extends (<G>() => G extends U ? 1 : 2) ? true : false;
这个类型通过高阶类型推断,能精准识别类型的细节差异(比如字符串字面量大小写、对象属性名/类型、联合成员数量等)。
然后实现编译期断言工具:
// 编译期断言:条件为false时触发报错 type StaticAssert<Condition extends boolean> = Condition extends true ? never : "Assertion failed: expected condition to be true"; // 使用示例 type _CheckStrEqual = StaticAssert<TypesAreEqual<TStrLit_0, TStrLit_1>>; // 无报错 type _CheckStrNotEqual = StaticAssert<TypesAreEqual<TStrLit_0, TStrLit_2>>; // 编译报错
如果断言失败,StaticAssert会返回一个非never的类型,TypeScript会在类型检查时抛出错误(需确保开启noUnusedLocals配置,让未使用的类型别名也被校验)。
模拟函数调用风格
如果想贴近示例中的static_assert(...)写法,可以用泛型函数约束:
const static_assert = <T extends true>(_: T) => {}; // 使用示例 static_assert(TypesAreEqual<TStrLit_0, TStrLit_1> as true); static_assert((!TypesAreEqual<TStrLit_0, TStrLit_2>) as true);
通过泛型约束T extends true,只有当传入的条件为true时才符合类型要求,否则会触发编译错误。
注意点
- 建议开启
strict模式,确保类型判断的准确性。 - 对于极端复杂的递归类型或动态条件类型,
TypesAreEqual可能存在局限性,但覆盖绝大多数常规类型场景足够。
内容的提问来源于stack exchange,提问作者Henry
相关产品推荐
相关产品推荐

