TypeScript:如何为字符串字面量类型扩展接口设计通用类型守卫?
通用类型守卫实现方案
首先要明确:你示例里的写法行不通,因为TypeScript的泛型T是编译时类型,运行时函数内部根本访问不到T.foo的字面量值——类型信息在编译后会被完全擦除,运行时没有任何关于T的痕迹。
不过我们可以通过以下几种方式实现你想要的通用类型守卫:
方式一:参数化通用守卫函数
先定义一个映射类型,把每个扩展接口和对应的foo字面量绑定,再写一个接收字面量参数的守卫:
interface A { foo: string } interface B extends A { foo: "bar" } interface C extends A { foo: "baz" } // 定义扩展接口与foo值的映射 type AExtensionMap = { bar: B; baz: C; // 新增扩展接口时,在这里补充对应的键值对即可 }; // 通用类型守卫函数 function isAExtension<K extends keyof AExtensionMap>( obj: A, targetFoo: K ): obj is AExtensionMap[K] { return obj.foo === targetFoo; } // 使用示例 function handleObj(obj: A) { if (isAExtension(obj, "bar")) { // obj 此时被推断为 B 类型 console.log(obj.foo); // 类型为 "bar" } else if (isAExtension(obj, "baz")) { // obj 此时被推断为 C 类型 console.log(obj.foo); // 类型为 "baz" } }
方式二:工厂函数生成单个守卫
如果更倾向于直接调用类似isB(obj)的守卫,可以用工厂函数批量生成:
// 守卫工厂函数 function createAExtensionGuard<T extends A>(expectedFoo: T["foo"]) { return (obj: A): obj is T => obj.foo === expectedFoo; } // 生成对应扩展接口的守卫 const isB = createAExtensionGuard<B>("bar"); const isC = createAExtensionGuard<C>("baz"); // 使用示例 function handleObj(obj: A) { if (isB(obj)) { // obj 推断为 B 类型 } else if (isC(obj)) { // obj 推断为 C 类型 } }
这两种方式都能实现“通过比较foo属性来区分扩展接口”的需求,且新增扩展接口时只需少量修改即可适配。
内容的提问来源于stack exchange,提问作者Freddy L.
相关产品推荐
相关产品推荐

