Description
TypeScript Version: 3.8.3
Search Terms: variance contravariance
Description: An Injector in typed-inject is a dependency injection container that is type safe. It can only provide values for tokens it knows. A small example:
declare const fooInjector: Injector<{foo: string}>; // this injector can provide string values for the token 'foo'
const foo = fooInjector.resolve('foo');
// => typeof foo === 'string'
fooInjector.resolve('bar');
// => Error ts(2345) Argument of type '"bar"' is not assignable to parameter of type '"foo"'
It makes sense that an injector Injector<{}>
is not assignable to Injector<{foo: string}>
, since it cannot provide a value for token 'foo'
. This was the case in TS 3.7. However, since TS 3.8, Injector<{}>
is assignable to Injector<{foo: string}>
😪.
declare const rootInjector: Injector<{}>;
const fooInjector: Injector<{ foo: string}> = rootInjector;
Expected behavior: Type 'Injector<{}>' is not assignable to type 'Injector<{ foo: string; }>'.
Actual behavior: No error
Related Issues: Couldn't find any 🤷♂️
Code
I think I've trimmed it down to the essentials.
interface Injector<TContext> {
injectFunction<Tokens extends (keyof TContext)[]>(todo:
(...args: { [K in keyof Tokens]: Tokens[K] extends keyof TContext ? TContext[Tokens[K]] : never; }) => void): void;
}
declare const rootInjector: Injector<{}>;
const fooInjector: Injector<{ foo: string}> = rootInjector;
Output
(none)
Compiler Options
{
"compilerOptions": {
"noImplicitAny": true,
"strictNullChecks": true,
"strictFunctionTypes": true,
"strictPropertyInitialization": true,
"strictBindCallApply": true,
"noImplicitThis": true,
"noImplicitReturns": true,
"alwaysStrict": true,
"esModuleInterop": true,
"declaration": true,
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
"moduleResolution": 2,
"target": "ES2017",
"jsx": "React",
"module": "ESNext"
}
}
Playground Link: Provided
Simpler contravariant examples like this still work as expected.
type Action<T> = (arg: T) => void;
declare let b: Action<{}>;
declare let a: Action<{foo: number}>;
b = a
// => Error! Type 'Action<{ foo: number; }>' is not assignable to type 'Action<{}>'.