- 
                Notifications
    
You must be signed in to change notification settings  - Fork 13.1k
 
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<{}>'.