doberkofler
doberkofler

Reputation: 10341

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

The following code snippet reports an error Type 'string' is not assignable to type 'never'.(2322) in the line obj[prop] = value and I struggle understanding why?

interface fooType {
    s: string,
    n: number,
}

function bar(value: string, obj: fooType, prop: keyof fooType) {
    if (typeof value === 'string') {
        obj[prop] = value;
    }
}

const foo = {s: '', n: 0};

bar('s', foo, 's');

console.log(foo);

Upvotes: 13

Views: 7876

Answers (3)

Alex Wayne
Alex Wayne

Reputation: 187014

prop: keyof fooType means that prop could be "s" or "n". But those two property values have incompatible types. So typescript tries to infer a value that could be assignable to both by finding the intersection of the types of the properties .s and .n. So it does: string & number, which have no overlap, so the type is never.

If you want to type an argument as a union (like "s" | "n") and you need to know which member of that union it is, then you need generics.

For generic parameters you need at least two here. One for the property name (we'll use K for key) you wish to update, and one for the value to make sure the it matches the property's type (we'll use V for value).

You can now drop the typeof value === 'string' because typescript will ensure the value is the right type for the property.

function bar<
    K extends keyof fooType,
    V extends fooType[K]
>(
    value: V,
    obj: fooType,
    prop: K
) {
    obj[prop] = value;
}

const foo = {s: '', n: 0};

bar('string', foo, 's'); // works
bar(123, foo, 'n'); // works


// Should be type errors:
bar('string', foo, 'n') // '"string"' is not assignable to parameter of type 'number'
bar(123, foo, 's') // '123' is not assignable to parameter of type 'string'.

Upvotes: 28

HTN
HTN

Reputation: 3604

fooType['s'] is type string, fooType['n'] is type number, so obj[prop] is type string & number because it's the only type assignable to obj[prop]. And string & number = never

Upvotes: 6

satanTime
satanTime

Reputation: 13539

you need to use generics here, then it can match the types correctly.

interface fooType {
    s: string,
    n: number,
}

function bar<
  OBJ extends fooType,
  KEY extends keyof OBJ,
  VAL extends OBJ[KEY]
>(value: VAL, obj: OBJ, prop: KEY) {
  obj[prop] = value;
}

const foo = {s: '', n: 0};

bar('s', foo, 's');

console.log(foo);

Upvotes: 1

Related Questions