Home > OS >  How to resolve 'X' is assignable to the constraint of type 'T', but 'T'
How to resolve 'X' is assignable to the constraint of type 'T', but 'T'

Time:01-11

I have this TypeScript playground:

export function isBlue<T extends Mesh>(
  object: unknown,
  type: T | Array<T>,
): object is BlueNodeType<T> {
  const array: Array<T> = Array.isArray(type) ? type : [type]
  return (
    object != null && typeof object === 'object' &&
    'type' in object &&
    typeof object.type === 'string' &&
    array.includes((object as BlueNodeType<T>).type)
  )
}

export enum Mesh {
  Assertion = 'mesh-assertion',
}

export type BlueBaseType = {
  color: 'blue'
}

export type BlueAssertionType = BlueBaseType & {
  type: Mesh.Assertion
}

export type BlueMappingType = {
  'mesh-assertion': BlueAssertionType
}

export type BlueNodeType<T extends Mesh> = BlueMappingType[T]

It is throwing this error:

Argument of type 'Mesh' is not assignable to parameter of type 'T'.
  'Mesh' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint 'Mesh'.(2345)

How do I get this to work? In my real codebase I have a BlueMappingType with 40 types, so I would like for it to be able to pick the correct type based on the generic type parameter.

CodePudding user response:

Using Array.prototype.includes is tricky one. In order to make it work, you can try my custom includes:

const withTuple = <
  List extends string[]
>(list: readonly [...List]) =>
  (prop: string): prop is List[number] =>
    list.includes(prop)

export function isBlue<T extends Mesh>(
  object: unknown,
  type: T | Array<T>,
): object is BlueNodeType<T> {
  const array: Array<T> = Array.isArray(type) ? type : [type]
  const includes = withTuple(array)
  return (
    object != null && typeof object === 'object' &&
    'type' in object &&
    typeof object.type === 'string' &&
    includes(object.type)
  )
}

export enum Mesh {
  Assertion = 'mesh-assertion',
}

export type BlueBaseType = {
  color: 'blue'
}

export type BlueAssertionType = BlueBaseType & {
  type: Mesh.Assertion
}

export type BlueMappingType = {
  'mesh-assertion': BlueAssertionType
}

export type BlueNodeType<T extends Mesh> = BlueMappingType[T]

Playground

withTuple is just a curried version of Array.prototype.includes but it works with tuples.

You can check my article for more examples

  • Related