Warm tip: This article is reproduced from stackoverflow.com, please click
generics types typescript

Typescript compiler never Error: Type 'string | number' is not assignable to type 'never'. Type 'str

发布于 2020-03-27 15:46:04

Here is a piece of code i got:

interface Obj {
  a: number
  b: string
}

const obj: Obj = {
  a: 1,
  b: 'hi'
}

function fn(key: keyof Obj, value: Obj[keyof Obj]) {
  let foo = obj[key]
  obj[key] = value
}

fn("a", 2)

So what i want to do is, i want the function fn() to be able to update the object obj properties, the first argument of this function is any key that obj has(defined in Obj interface), and the second argument is the value you wanna give to that key.

However, the typescript popup with an error in the obj[key] = value this line, which is:

Type 'string | number' is not assignable to type 'never'.
  Type 'string' is not assignable to type 'never'.(2322)

Here is a screenshot:

error message

And a strange thing happens here, if you hover to the variable foo(which is line 13 in the picture), it says:

let foo: string | number

which means, obj[key]'s type is string | number, but the error says obj[key] type is never.

so my first question is: How come a string | number type magically becomes a never type? Is there any way to fix this?

Then i got another piece of code which solves this problem:

interface Obj {
  a: number
  b: string
}

const obj: Obj = {
  a: 1,
  b: 'hi'
}

function fn<K extends keyof Obj>(key: K, value: Obj[K]) {
  obj[key] = value
}

fn("a", 2)

Therefore my second question would be: why using Generics solve the problem and what the hack is the keyword extends here?

BTW, all the code are tested in typescript 3.7.5 version.

I am not a native English speaker, hope i explained my confusion clearly.

Questioner
Limboer
Viewed
96
Valeriy Katkov 2020-01-31 20:21

What about the error, it's because keyof Obj might be "a" or "b" which have type number or string. In the expression obj[key] the compiler doesn't know the property type, it might be number or string as well, so it disallows such assignment. Here is the same question. And you can find the explanation here, see Fixes to unsound writes to indexed access types.

In case of the generic function K extends keyof Obj means that K type can be "a" or "b" as well, but when you call the function fn("a", 2) you implicitly set K to "a", the compiler infers K type from the first argument. So now, inside the call context key has "a" type and Obj[K] is number, hence the assignment becomes correct.


I just tried to explain the difference to my wife, who isn't a programmer :) I think it might be helpfull too:

Usual function: Lets imagine you are eating a cake but your eyes are closed. You know that it might be a cherry cake or a banana cake. You like the taste but you cannot say "What a delicious banana cake!" because you are not sure that it's a banana cake.

Generic function: In this case you eyes are open and you can choose the cake you want to eat, but you still have two choices: cherry or banana. Now, if you've chosen the banana cake and tasted it, you can say "What a delicious banana cake!".