Typescript泛型+条件类型无法分配给类型错误

时间:2019-03-21 05:53:10

标签: typescript typescript-generics

我正在使用条件类型通过类泛型自动推断数据结构的类型。由于某种原因,它不能推断ObjectType构造函数中的类型。

Typescript playground

   export type NodeType<T> = T extends (infer U)[] ? ArrayNode<U> : ObjectNode<T>

export abstract class Node {}
export class ObjectNode<T> extends Node {
  constructor(public fields: { [key in keyof T]: Field<NodeType<T[key]>> }) {
    super()
  }
  public data: T
}
export class ArrayNode<T> extends Node {
  public data: T[]

  constructor(public ofType: NodeType<T>) {
    super()
  }
}

class Field<T extends Node> {
  constructor(public node: T) {}
}

const User = new ObjectNode({})

const query = new ObjectNode({
  user: new Field(User),
  // ****************
  /// The below `users` field should be automatically be detected as 'Field<ArrayNode<{}>>', but for some reason it's 'Field<ObjectNode<{}>>'.

  /// Property 'fields' is missing in type 'ArrayNode<{}>' but required in type 'ObjectNode<{}>'.
  // ****************
  users: new Field(new ArrayNode(User))
})

var q: ObjectNode<{ users: {}; user: {} }>

q.fields.users.node.fields
q.fields.user.node.fields
q.data.user
q.data.users

query.fields.users.node.fields
query.fields.user.node.fields
query.data.user
query.data.users

1 个答案:

答案 0 :(得分:1)

我认为打字稿不能遵循条件类型的逻辑来提取T。您需要反转逻辑并将节点类型作为类型参数,并手动从节点类型扩展数据类型。解决方案可能如下所示:

export abstract class Node { }
type AllNodeTypes = ArrayNode<any> | ObjectNode<any>

export type NodeDataType<T extends AllNodeTypes> = T['data']; // can be a conoditional type with infer if needed (ie if not all node types have a data field)
export type ObjectNodeDataType<T extends Record<keyof T, Field<AllNodeTypes>>> = {
  [P in keyof T]:NodeDataType<T[P]['node']>
} 
export class ObjectNode<TNode extends Record<keyof TNode, Field<AllNodeTypes>>> extends Node {
  constructor(public fields: TNode) {
    super()
  }
  public data: ObjectNodeDataType<TNode>
}
export class ArrayNode<TNode extends AllNodeTypes> extends Node {
  public data: NodeDataType<TNode>[]

  constructor(public ofType: TNode) {
    super()
  }
}

class Field<T extends Node> {
  constructor(public node: T) { }
}

const User = new ObjectNode({})

const query = new ObjectNode({
  user: new Field(User),
  users: new Field(new ArrayNode(User))
})

query.fields.users.node.ofType.fields
query.fields.user.node.fields
query.data.user
query.data.users