使用io-ts实现泛型品牌类型是否可行?
实现方案:基于io-ts的泛型令牌类型
你的需求是可行的,核心思路是通过泛型品牌标记将令牌字符串与返回类型关联,同时保留io-ts的运行时验证能力。以下是调整后的实现代码:
1. 定义泛型品牌标记
首先创建一个带泛型的品牌接口,用于在类型层面关联令牌对应的返回类型:
import * as t from 'io-ts'; // 泛型品牌标记,携带令牌对应的返回类型信息 interface TokenNameBrand<T> { readonly TokenName: unique symbol; readonly Type: T; }
2. 创建io-ts运行时验证器
运行时层面,我们只需要验证输入是字符串(因为你允许任意合法字符串作为令牌),所以品牌验证逻辑保持简单:
// 运行时的io-ts brand类型,不涉及泛型T const TokenNameRuntime = t.brand( t.string, (n: unknown): n is t.Branded<string, TokenNameBrand<unknown>> => typeof n === 'string', // 任意字符串都符合令牌要求 'TokenName' ); // 泛型类型别名,将令牌字符串与返回类型T绑定 export type TokenName<T> = t.Branded<string, TokenNameBrand<T>>;
3. 创建类型化令牌常量
通过类型断言将字符串转换为带泛型的令牌类型:
const WEIGHT = "weight" as TokenName<number>; const SPECIES = "species" as TokenName<string>;
4. 实现类型安全的evaluateToken函数
借助映射类型确保令牌与返回类型的一致性,让TypeScript自动推断返回类型:
// 定义令牌到值的映射,确保类型匹配 type TokenValueMap = { weight: number; species: string; }; function evaluateToken<K extends keyof TokenValueMap>( name: TokenName<TokenValueMap[K]> ): TokenValueMap[K] | null { const tokenValues: TokenValueMap = { weight: 1200, species: "bear" }; // 类型断言:因为我们控制了令牌的创建,name一定是TokenValueMap的key return tokenValues[name as keyof TokenValueMap] ?? null; }
验证效果
此时调用evaluateToken会自动推断正确的返回类型:
const w = evaluateToken(WEIGHT); // w的类型被推断为 number | null const s = evaluateToken(SPECIES); // s的类型被推断为 string | null
关键说明
- 运行时:io-ts的
TokenNameRuntime负责验证令牌是合法字符串,确保输入符合基本要求。 - 类型层面:通过泛型
TokenName<T>将令牌与返回类型绑定,结合TokenValueMap确保令牌和值的类型一一对应。 - 可控性:由于你负责令牌的创建(如
WEIGHT、SPECIES),只要保证令牌名称与TokenValueMap的key一致,就能维持类型安全。
内容的提问来源于stack exchange,提问作者Michael Lorton
相关产品推荐
相关产品推荐

