Home > Back-end >  Typescript - Type 'string | number' is not assignable to type 'never'
Typescript - Type 'string | number' is not assignable to type 'never'

Time:07-19

I can't seem to figure it out how to solve it. It is throwing the following error: Type 'string | number' is not assignable to type 'never'.

This is my code. Any idea or any tips are much appreciated.


interface User {
    name: string
    age: number
}

const user: User = {
    name: "someone",
    age: 20
}

const getValue = (key: keyof User) => {
    return user[key]
}

const setValue = (key: keyof User, value: string | number) => {

    let _user: User = Object.assign({}, user);
    return _user[key] = value;  <---- error is thrown here
}

CodePudding user response:

const setValue = <K extends keyof User>(key: K, value: User[K]) => {
  let _user: User = Object.assign({}, user);
  return _user[key] = value;
}

Then you can get perfect type checking

setValue('name', 18); // ❌
setValue('name', 'yes'); // ✅
setValue('age', '18'); // ❌
setValue('age', 18); // ✅
setValue('otherKey', 'something'); // ❌

CodePudding user response:

The way in which you have declared your types in the setValue function results in Typescript correctly determining that there are no possible values of type _user[key].

Let's explore this in more detail.

The Problem

Your User interface contains two properties, one of type string and one of type number.

The key parameter of your setValue function is of type keyof User.

All good so far...

However, at 'compile-time' Typescript has no way to determine which key is being provided for lookup, and therefore cannot determine which specific type _user[key] is.

What Typescript does know at compile-time, is that is either of type string or number. Therefore it infers the intersection type string & number.

Which set of types are both strings and numbers?

Correct - there are no such types; Typescript therefore correctly infers that the type of _user[key] is the empty set of types - known as the never type.

Simple Demos


Firstly, open your favorite IDE with Typescript hinting, and define a variable with an intersection type:

let a : string & number;

Descriptively, this is saying that the the type of a is the set of all possible types that are both string and number.

Typescript correctly infers that there are, in fact, no such types, and a is therefore of type never.

VS Code screenshot


Conversely, imagine that your User interface instead had both properties of type string.

interface User {
    name: string
    age: string
}

In this case, your code would work fine, if you pass in and use only strings.

This is equivalent to saying that the type of _user[key] is the intersection type string & string - which of course is simply string;

VS Code screenshot 2


The Solution

In order to have correct type inference in your setValue function, you need to provide a generic type, that you can make use of for both the key and value parameters of your setValue function:

const setValue = <K extends keyof User>(key: K, value: User[K]) => {
  const _user: User = Object.assign({}, user); // Consider also the ES6 spread operator here
  _user[key] = value;
  return _user;
};

Or, if you want an entirely generic setValue function, independent of a specific type or object, your function signature could be as follows

const setValueOnObj = <T, K extends keyof T>(obj: T, key: K, value: T[K]) => [...]
  • Related