有没有办法通过泛型将具有相同联合类型签名的函数参数缩小到完全相同的子类型

Is there way to narrow function parameters witch have same union types signature to exactly same subtypes by generic type

// this is what i what for function parameters type
type Combinable = string | number;

function isString(param: unknown): param is string {
  return typeof param === "string";
}

// i expect function parameters a and b to have exactly the same subtype of Combinable like both are string or number(e.g. a: string and b: number is not i wanted),and finally the return type of this function must be the same subtype of a and b

function add<T extends Combinable>(a: T, b: T): T {
  if (isString(a)) {
    //and i get this following error
    //1st question: how can i fix this

    /* Type 'string' is not assignable to type 'T'.
  'string' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint 'Combinable'.ts(2322) */

  // my 2nd question is here: why TS can not infer b must be type "string" since i narrow type of a to "string"?
    //type ta = typeof a  // answer is  string & T === string
    //type tb = typeof b  // answer is T     why T not string?
    return a + b;
  } else {
    return (a as number) + (b as number); //same error
  }
}

let a: Combinable = "123";
let b: Combinable = 123;

//3rd question here: i want to know that why TS can infer a and b must be the same subtype of Combinable here but it can not do this in the function body above?
add(a, b);  //constraint a and b in same sub type is what i expected when function called

我的问题:

  1. 我该如何解决这个问题
  2. 为什么 TS 无法推断 b 必须是“string”类型,因为我将 a 的类型缩小为“string”?
  3. 为什么TS在调用函数时可以推断出a和b必须是Combinable的同一个子类型,而在上面的函数体中却不能呢?

感谢您的评论和回答。

如果只是为了确保打字稿编译器完成他的工作,你可以这样实现:

type Combinable = string | number;

function add<T extends Combinable>(a: T, b: T): T {
  return (a as number) + (b as number) as T;
}

let a: Combinable = "123";
let b: Combinable = 123;
const sumOfAB = add(a, b); // Argument of type 'number' is not assignable to parameter of type 'string'.(2345)

let c: Combinable = 123;
let d: Combinable = "123";
const sumOfCD = add(c, d); // Argument of type 'string' is not assignable to parameter of type 'number'.(2345)

let e: Combinable = 123;
let f: Combinable = 123;
const sumOfEF = add(e, f); // const sumOfEF: number

let g: Combinable = "123";
let h: Combinable = "123";
const sumOfGH = add(g, h); // const sumOfGH: string