Обобщения типа Types + условные типы не могут быть назначены для ошибки типа - PullRequest
1 голос
/ 21 марта 2019

Я использую условные типы для автоматического вывода типа структуры данных через обобщенные классы.По какой-то причине он не выводит типы в конструкторе ObjectType.

%20%3D%20T%20extends%20(infer%20U)%5B%5D%20%3F%20ArrayNode%20%3A%20ObjectNode%0D%0A%0D%0Aexport%20abstract%20class%20Node%20%7B%20%7D%0D%0Aexport%20class%20ObjectNode%20extends%20Node%20%7B%0D%0A%20%20constructor(public%20fields%3A%20%7B%20%5Bkey%20in%20keyof%20T%5D%3A%20Field>%20%7D)%20%7B%0D%0A%20%20%20%20super()%0D%0A%20%20%7D%0D%0A%20%20public%20data%3A%20T%0D%0A%7D%0D%0Aexport%20class%20ArrayNode%20extends%20Node%20%7B%0D%0A%20%20public%20data%3A%20T%5B%5D%0D%0A%0D%0A%20%20constructor(public%20ofType%3A%20NodeType)%20%7B%0D%0A%20%20%20%20super()%0D%0A%20%20%7D%0D%0A%7D%0D%0A%0D%0Aclass%20Field%20%7B%0D%0A%20%20constructor(public%20node%3A%20T)%20%7B%20%7D%0D%0A%7D%0D%0A%0D%0Aconst%20User%20%3D%20new%20ObjectNode(%7B%7D)%0D%0A%0D%0Aconst%20query%20%3D%20new%20ObjectNode(%7B%0D%0A%20%20user%3A%20new%20Field(User)%2C%0D%0A%20%20%2F%2F%20****************%0D%0A%20%20%2F%2F%2F%20The%20below%20%60users%60%20field%20should%20be%20automatically%20be%20detected%20as%20'Field>'%2C%20but%20for%20some%20reason%20it's%20'Field>'.%0D%0A%0D%0A%20%20%2F%2F%2F%20Property%20'fields'%20is%20missing%20in%20type%20'ArrayNode<%7B%7D>'%20but%20required%20in%20type%20'ObjectNode<%7B%7D>'.%0D%0A%20%20%2F%2F%20****************%0D%0A%20%20users%3A%20new%20Field(new%20ArrayNode(User))%0D%0A%7D)%0D%0A%0D%0Avar%20q%3A%20ObjectNode<%7B%20users%3A%20%7B%7D%3B%20user%3A%20%7B%7D%20%7D>%0D%0A%0D%0Aq.fields.users.node.fields%0D%0Aq.fields.user.node.fields%0D%0Aq.data.user%0D%0Aq.data.users%0D%0A%0D%0Aquery.fields.users.node.fields%0D%0Aquery.fields.user.node.fields%0D%0Aquery.data.user%0D%0Aquery.data.users%0D%0A" rel="nofollow noreferrer"> Типизированная площадка

   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 Ответ

1 голос
/ 21 марта 2019

Я не думаю, что машинопись может следовать логике условного типа для извлечения 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
...