In this code the type of array is already defined but Typescript fires an error for the array push, saying that:
Argument of type 'string | number' is not assignable to parameter of type 'never'. Type 'string' is not assignable to type 'never'.
type Characters = {
letters: string[];
numbers: number[];
}
const characters :Characters = {
letters: ['a', 'b',' c' ],
numbers: [1,2,3],
}
type AddCharacter = <K extends keyof Characters, V extends Characters[K][number]>
(characterType:K, character:V) => void;
const addCharacter :AddCharacter = (characterType, character) =>{
characters[characterType].push(character) //Type 'string' is not assignable to type 'never'
}
addCharacter('letters', 'd') //ok
addCharacter('numbers', 4) //ok
addCharacter('letters', 4) //error as expected beacause 4 is not a string
addCharacter('numbers', 'd') //error as expected beacause d is not a number
addCharacter('words', 'test' ) //error as expected because 'words' is not a key of 'Characters'
I cannot figure out why it showing error since the type seem to be correct (got perfect type checking calling addCharacter
).
How can I get rid of the error and getting it to work nicely with typescript?
Thanks for any help!
Playground
CodePudding user response:
If you insert the following line into the body of addCharacter
,
let x = characters[characterType][1]
You'll see that Typescript infers the type of x
to be string | number
, which implies that characters[characterType]
is string[] | number[]
.
That would explain why, if you inspect the type of the called push
method, the items
parameter has type never[]
,
(method) Array<T>.push(items: never[]): number
because T
is string[] | number[]
, not just string[]
or just number[]
. This causes the type of items
to be never[]
, which in turn results in the error message you see.
You can fix the issue by changing addCharacter
to:
const addCharacter:AddCharacter = <K extends keyof Characters, V extends Characters[K][number]>(characterType: K, character: V) => {
(characters[characterType] as V[]).push(character)
}
This forces the type of characters[characterType]
to be the dynamically inferred type of V
based on the type of K
. I don't know why Typescript doesn't do this automatically. Perhaps a Typescript guru can explain. Or perhaps we should file a Typescript Issue.