Home > other >  why typescript behaves differently when using the type directly vs using generics with extends?
why typescript behaves differently when using the type directly vs using generics with extends?

Time:01-10

I dont understand why the code behaves differently when using generics.
The topic is based on the answer for How to create a relation between parameters of a function?

interface Data {
  a: number,
  b: { x: number, y: number}
}

type GetKeyValueTuples<T> = { [Key in keyof T]: [Key, T[Key]] }[keyof T];

function getChangedDataByProperty<T extends Data>(
  ...[data, changedProp, newPropValue]: [T, ...GetKeyValueTuples<T>]
) {
  if (changedProp === "b") {
    changedProp
    return {
      ...data,
      b: {
        // why this has number, a, b types?
        x: newPropValue.x,
        y: newPropValue.y,
      }
    }
  } else {
    return {
      ...data,
      x: newPropValue
    }
  }
}

// why this behaves differently than the abvove function?
function getChangedDataByProperty2(
  ...[data, changedProp, newPropValue]: [Data, ...GetKeyValueTuples<Data>]
) {
  if (changedProp === "b") {
    changedProp
    return {
      ...data,
      b: {
        x: newPropValue.x,
        y: newPropValue.y,
      }
    }
  } else {
    return {
      ...data,
      x: newPropValue
    }
  }
}

The ts playground

CodePudding user response:

The getChangedDataByProperty2() function's rest parameter is of a discriminated union type, and since TypeScript 4.6 we've been able to destructure such discriminated unions into separate parameters to get the narrowing behavior you're seeing, where a check of the discriminant changedProp narrows the type of newPropValue.

On the other hand, getChangedDataByProperty()'s rest parameter is of a generic type which is ultimately constrained to the same discriminated union type. For whatever reason, TypeScript does not consider a generic type to be an acceptable discriminant for a discriminated union. There is an issue filed at microsoft/TypeScript#50652 about this, but it's currently marked as "needs investigation" so there's no official word on whether this is a bug, a design limitation, or can be interpreted as a feature request. It's also on the backlog, which means that such official word might not be forthcoming.

  • Related