英文:
TypeScript make both arguments of a function match their type argument
问题
I've translated the code part for you:
我不确定如何为这个特定问题创建一个描述性标题,对此感到抱歉。
在 Angular 中(只需了解 TypeScript 即可回答此问题),我已经创建了几个实用函数,以确保在设置提供程序时使用了正确的类型。因此,我创建了 **createValueProvider**、**createClassProvider** 和 **createFactoryProvider**,它们基本上确保第一个参数(令牌 / 类型,即 **提供属性**)和第二个参数(useValue / useClass / useFactory)具有匹配的类型。
例如 **createValueProvider** 的示例,它可以工作:
```typescript
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'); // OK
createValueProvider(MY_TOKEN, 123); // Error: Argument of type 'number' is not assignable to parameter of type 'string'.
interface Base {
prop: string;
}
class BaseImpl implements Base {
prop = '';
}
createValueProvider(BaseImpl, { prop: '' }); // OK
createValueProvider(BaseImpl, { prop: 123 }); // Error: Type 'number' is not assignable to type 'string'.
因此,上面的代码验证了在将令牌或类型作为第一个参数给定时,第二个参数必须是第一个参数提供的类型参数的值。
我无法使下面的函数 createExistingProvider 正常工作,其中第二个参数必须与第一个参数的类型相似。通过代码更容易解释。让我们简化问题,只考虑 InjectionToken 并忽略 Type:
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); // OK(都是 InjectionToken<string>)
有没有办法让上面的代码在编译时抛出错误?谢谢!
<details>
<summary>英文:</summary>
I'm not sure how to make a descriptive title for this particular question, sorry for that.
In Angular (this question can be answered by only knowing TypeScript), I have created several utility functions to ensure that when we setup providers, the correct types are used. So I create a **createValueProvider**, **createClassProvider** and **createFactoryProvider**, they basically ensure that the first argument (the token / Type, aka **provide property**) and the second argument (useValue / useClass / useFactory) has matching types.
Example for **createValueProvider**, which works:
```typescript
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'); // OK
createValueProvider(MY_TOKEN, 123); // Error: Argument of type 'number' is not assignable to parameter of type 'string'.
interface Base {
prop: string;
}
class BaseImpl implements Base {
prop = '';
}
createValueProvider(BaseImpl, { prop: '' }); // OK
createValueProvider(BaseImpl, { prop: 123 }); // Error: Type 'number' is not assignable to type 'string'.
So the above code validates that given a token or type as first argument, the second has to be a value of the type argument of whatever is provided as the first argument.
I can't make it work with the following function createExistingProvider, for which the second argument as to be of a similar type as the first. It's easier to explain with code. Let's simplify the problem with just InjectionToken and ignore Type:
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); // Should fail, but doesn't (one is InjectionToken<number> and other InjectionToken<string>)
createExistingProvider(SECOND_NEW_TOKEN, EXISTING_TOKEN); // OK (both InjectionToken<string>)
Is there a way to make the above code throw me a compilation error?
Thanks in advance!
答案1
得分: 1
这可能不太可能,因为注入令牌的通用参数仅在构造函数中使用(还有另一个地方,但该方法标记为内部)。因此,从任何版本的 InjectionToken
得到的结果类型都变成了 TypeScript 中的相同类型。
我尝试演示一个通用字段的存在如何改变 TypeScript 行为的示例 playground。
免责声明:我不确定是否正确,但似乎是这样。
英文:
This might not be possible since the generic parameter of the injection token is only used in the constructor(And in another place, but that method is marked internal). So the resulting type from any version of InjectionToken
becomes the same type to typescript.
I try to demonstrate how the existence of a generic field changes how typescript behaves in this playground.
Disclaimer: I am not sure if I am correct, but it seems like it.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论