Typescript: Type 'K' is not assignable to type 'V (extends K)'

Created on 13 Jan 2019  路  3Comments  路  Source: microsoft/TypeScript


TypeScript Version: 3.3.0-dev.20190112


Search Terms: type generic is not assignable to type generic extends generic

Code

export const defaultTo = <T, U>(value: T, defaultValue: U) =>
  value == null ? defaultValue : value;

export const propOr = <
  K extends number | string,
  V extends K,
  T extends { [key in K]: V }
>(
  object: T,
) => (key: K, defaultValue: V = key) =>
  key == null ? defaultValue : defaultTo((object || {})[key], defaultValue);

tsconfig.json

{
  "compilerOptions": {
    "jsx": "react",
    "target": "esnext",
    "moduleResolution": "node",
    "allowSyntheticDefaultImports": true,
    "strict": true
  },
  "include": ["src/**/*"]
}

Expected behavior:

defaultValue: V = key should work without errors (even in strict mode).

Playground Link: http://www.typescriptlang.org/play/#src=export%20const%20defaultTo%20%3D%20%3CT%2C%20U%3E(value%3A%20T%2C%20defaultValue%3A%20U)%20%3D%3E%0D%0A%20%20value%20%3D%3D%20null%20%3F%20defaultValue%20%3A%20value%3B%0D%0A%0D%0Aexport%20const%20propOr%20%3D%20%3C%0D%0A%20%20K%20extends%20number%20%7C%20string%2C%0D%0A%20%20V%20extends%20K%2C%0D%0A%20%20T%20extends%20%7B%20%5Bkey%20in%20K%5D%3A%20V%20%7D%0D%0A%3E(%0D%0A%20%20object%3A%20T%2C%0D%0A)%20%3D%3E%20(key%3A%20K%2C%20defaultValue%3A%20V%20%3D%20key)%20%3D%3E%0D%0A%20%20key%20%3D%3D%20null%20%3F%20defaultValue%20%3A%20defaultTo((object%20%7C%7C%20%7B%7D)%5Bkey%5D%2C%20defaultValue)%3B

Most helpful comment

The error message is correct here, but the error could be improved and this is tracked by #29049.

The reason why the error is correct is because key is only known to satisfy the constraint of V, but key might not satisfy V itself. In other words, key is assignable to the upper bound of V, but for this to be safe you need key to be assignable to all instantiations of V, which you don't know.

A concrete example:

function example<T extends number>(x: T = 10) {
    return;
}

example<3>();

The value 10 is assignable to the constraint of T, but it is not assignable to this particular instantiation of T. If there was no error I would passing 10 where 3 is expected!

Your example is essentially the same, except with an intermediate type variable; the same idea applies transitively though.

function example2<T extends number, U extends T>(x: T, y: U = x) {
    return;
}

example2<number, 3>(10);

All 3 comments

The error message is correct here, but the error could be improved and this is tracked by #29049.

The reason why the error is correct is because key is only known to satisfy the constraint of V, but key might not satisfy V itself. In other words, key is assignable to the upper bound of V, but for this to be safe you need key to be assignable to all instantiations of V, which you don't know.

A concrete example:

function example<T extends number>(x: T = 10) {
    return;
}

example<3>();

The value 10 is assignable to the constraint of T, but it is not assignable to this particular instantiation of T. If there was no error I would passing 10 where 3 is expected!

Your example is essentially the same, except with an intermediate type variable; the same idea applies transitively though.

function example2<T extends number, U extends T>(x: T, y: U = x) {
    return;
}

example2<number, 3>(10);

@jack-williams Thank you for the explanation, I'm closing this issue.

@kripod No problem!

Was this page helpful?
0 / 5 - 0 ratings

Related issues

CyrusNajmabadi picture CyrusNajmabadi  路  3Comments

siddjain picture siddjain  路  3Comments

manekinekko picture manekinekko  路  3Comments

jbondc picture jbondc  路  3Comments

MartynasZilinskas picture MartynasZilinskas  路  3Comments