You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何实现TypeScript中createExistingProvider的参数类型匹配校验?

实现Angular InjectionToken的Existing Provider类型校验

我在Angular环境下用TypeScript实现了createValueProvider、createClassProvider等工具函数,用于确保配置Provider时的参数类型正确性。其中createValueProvider已经能正常完成类型校验,示例代码如下:

type TokenValue<Token> = Token extends InjectionToken<infer V> ? V : never;
type TypeValue<Type> = Type extends new (...args: unknown[]) => infer R ? R : never;

export const createValueProvider = <Token extends InjectionToken<any> | Type<any>>(provide: Token, useValue: TokenValue<Token> | TypeValue<Token>): ValueProvider => {
    return {
        provide,
        useValue,
    };
};

const MY_TOKEN = new InjectionToken<string>('My string');
createValueProvider(MY_TOKEN, 'Test'); // 正常通过
createValueProvider(MY_TOKEN, 123); // 报错:类型'number'不能分配给类型'string'

interface Base {
    prop: string;
}
class BaseImpl implements Base {
    prop = '';
}

createValueProvider(BaseImpl, { prop: '' }); // 正常通过
createValueProvider(BaseImpl, { prop: 123 }); // 报错:类型'number'不能分配给类型'string'

但createExistingProvider无法实现类似的类型校验,当前简化后的代码如下:

export const createExistingProvider = <Token extends InjectionToken<any>>(
    provide: Token,
    useExisting: Token extends InjectionToken<infer Value> ? InjectionToken<Value> : never
): Provider => {
    return {
        provide,
        useExisting,
    };
};

const EXISTING_TOKEN = new InjectionToken<string>('Existing');
const NEW_TOKEN = new InjectionToken<number>('New');
const SECOND_NEW_TOKEN = new InjectionToken<string>('Second New');

createExistingProvider(NEW_TOKEN, EXISTING_TOKEN); // 预期报错但实际不报错(一个是InjectionToken<number>,另一个是InjectionToken<string>)
createExistingProvider(SECOND_NEW_TOKEN, EXISTING_TOKEN); // 正常通过

当前问题是:当第一个参数为InjectionToken<number>、第二个为InjectionToken<string>时,不会触发编译错误,需要修改实现来达成预期的类型校验。

解决方案

修改createExistingProvider的泛型定义,明确约束useExisting的Token值类型必须与provide的Token值类型完全一致,有两种可行写法:

写法一:直接将Token值类型作为泛型参数

export const createExistingProvider = <Value>(
    provide: InjectionToken<Value>,
    useExisting: InjectionToken<Value>
): Provider => {
    return {
        provide,
        useExisting,
    };
};

写法二:通过条件类型提取provide的Token值类型并约束useExisting

export const createExistingProvider = <Token extends InjectionToken<any>>(
    provide: Token,
    useExisting: InjectionToken<Token extends InjectionToken<infer V> ? V : never>
): Provider => {
    return {
        provide,
        useExisting,
    };
};

修改后的效果

使用修改后的函数后,之前的错误场景会正确触发编译错误:

const EXISTING_TOKEN = new InjectionToken<string>('Existing');
const NEW_TOKEN = new InjectionToken<number>('New');
const SECOND_NEW_TOKEN = new InjectionToken<string>('Second New');

createExistingProvider(NEW_TOKEN, EXISTING_TOKEN); // 报错:类型'InjectionToken<string>'不能分配给类型'InjectionToken<number>'
createExistingProvider(SECOND_NEW_TOKEN, EXISTING_TOKEN); // 正常通过

原写法失效原因

原写法中,useExisting的类型是基于泛型参数Token的条件类型,但TypeScript在处理泛型条件类型时,不会即时将Token解析为具体的InjectionToken<Value>实例,而是保留条件类型的抽象形式,导致任何InjectionToken<any>都能匹配,因此无法触发类型不匹配的错误。修改后的写法通过明确绑定两个参数的Token值类型,确保了类型校验的有效性。

内容的提问来源于stack exchange,提问作者Mick412

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.14 04:02:18