1

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.

Lance
  • 75,200
  • 93
  • 289
  • 503
  • Does this answer your question? [How to fix TS2322: "could be instantiated with a different subtype of constraint 'object'"?](https://stackoverflow.com/questions/56505560/how-to-fix-ts2322-could-be-instantiated-with-a-different-subtype-of-constraint) – dwjohnston Jan 11 '23 at 04:32
  • Nope, I can't figure it out from that. – Lance Jan 11 '23 at 04:47

1 Answers1

1

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