Skip to content

Contravariance broke in 3.8 when combining spreading parameter list with conditional type #37400

Description

@nicojs

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:

declareconstfooInjector: Injector<{foo: string}>;// this injector can provide string values for the token 'foo'constfoo=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}> 😪.

declareconstrootInjector: Injector<{}>;constfooInjector: 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.

interfaceInjector<TContext>{injectFunction<Tokensextends(keyofTContext)[]>(todo:
(...args: {[KinkeyofTokens]: Tokens[K]extendskeyofTContext ? TContext[Tokens[K]] : never;})=>void): void;}declareconstrootInjector: Injector<{}>;constfooInjector: 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.

typeAction<T>=(arg: T)=>void;declareletb: Action<{}>;declareleta: Action<{foo: number}>;b=a// => Error! Type 'Action<{ foo: number; }>' is not assignable to type 'Action<{}>'.

Metadata

Metadata

Labels

BlockedA fix for this issue is blocked on another fix being in placeBugA bug in TypeScriptRescheduledThis issue was previously scheduled to an earlier milestone

Type

No type

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions