Home > database >  Argument of type 'string | number' is not assignable to parameter of type 'never'
Argument of type 'string | number' is not assignable to parameter of type 'never'

Time:01-06

Snippet and error:

const methods = {
  a(value: number) {},
  b(value: string) {}
};

function callMethodWithArg(methodAndArg: { method: 'a'; arg: number; } | { method: 'b'; arg: string; }) {
  methods[methodAndArg.method](methodAndArg.arg);
}

Argument of type 'string | number' is not assignable to parameter of type 'never'. Type 'string' is not assignable to type 'never'.

Looks like typescript isn't intelligent enough to figure out that method a can only be called with a number and method b can only be called with a string.

Any suggestions how to type this properly?

Playground

CodePudding user response:

I'm not sure if there is a cleaner solution, but this will work if you don't have too many cases:

const methods = {
  a(value: number) {},
  b(value: string) {}
};

function callMethodWithArg(methodAndArg: { method: 'a'; arg: number; } | { method: 'b'; arg: string; }) {
  if (methodAndArg.method === 'a') {
    // now it knows that method has to be 'a' and arg is a number
    methods[methodAndArg.method](methodAndArg.arg)
  } else if (methodAndArg.method === 'b') {
    methods[methodAndArg.method](methodAndArg.arg)
  }
}

Playground link

CodePudding user response:

My solution would be to use Type Predicates to cast the method as either Shape A or B (or however many shapes you need)

type MethodA = (value: number) => void
type MethodB = (value: string) => void

type ArgA = { method: 'a'; arg: number; }
type ArgB = { method: 'b'; arg: string; }

type Methods = {
  [key: string]: MethodA | MethodB
}

const methods: Methods = {
  a(value: number) { },
  b(value: string) { }
};


function callMethodWithArg(methodAndArg: ArgA | ArgB) {
  const method = methods[methodAndArg.method]

  if (determinShape(methodAndArg)) {
    (method as MethodA)(methodAndArg.arg)
  } else {
    (method as MethodB)(methodAndArg.arg)
  }
}


/** Will cast methodAndArg as ArgA if true, else ArgB */
function determinShape(methodAndArg: ArgA | ArgB): methodAndArg is ArgA {
  return Number.isFinite(methodAndArg.arg)
}

TS Playground

  •  Tags:  
  • Related