Используйте предполагаемый тип общего параметра одной функции для ввода других функций - PullRequest
0 голосов
/ 24 сентября 2019

Я создаю API для визуализации графа схемы.

Вот как это выглядит (упрощенно)

interface Node {
  name: string;
}

type NodeNames<T extends Node[]> = T[number]["name"]; // All node names as union of string literal

type Scalars = 'Int' | 'Boolean' | 'String'

type AllTypes<T extends Node[]> = NodeNames<T> | Scalars

interface Schema<T extends Node[]> {
  createNode(name: String, fields: Record<string, AllTypes<T>>): Node;

  render(types: T[]): string;
}

const s: Schema = { // generic not captured => "TypeError: Generic type 'Schema<T>' requires 1 type"

  render(types) {
    // print types
    return ''
  },
  createNode(name, fields) {
    return { name, fields };
  }
};

const Blog = s.createNode("Blog", { users: "User" });
const User = s.createNode("User", { posts: "Post" });
const Post = s.createNode("Post", { title: "String", comments: 'Comment' }); // <== Expected type error because 'Comment' node doesn't exist

s.render([Blog, User, Post]);

Я хотел бы убедиться, чтонельзя ссылаться на тип, который не был зарегистрирован в Schema.render из функции Schema.createNode.

В приведенном выше примере fields в основном должен иметь тип: Record<string, Scalars | 'User' | 'Post' | 'Blog', гдеUser | Post | Blog выводится из узлов, переданных в s.render

. Для этого я хотел бы вывести имена узлов, переданных в функцию render, чтобы ввести значенияparam fields из Schema.createNode.

К сожалению, дженерики фиксируются параметрами функции только в том случае, если они объявлены на уровне функций, а не если они объявлены на уровне интерфейса (что имеет смысл).

Как я могу использовать логический вывод s.render<T extends Node[]>(nodes: T) для ввода функции createNode(name: string, fields: AllTypes<T>), где T - это один и тот же логический тип, общий для двух функций?

Есть ли способ заставить эту работу работать?(даже с немного другим API)

Спасибо ?

1 Ответ

0 голосов
/ 24 сентября 2019

Я не думаю, что вы можете заставить это работать с несколькими вызовами.Лучшее решение - создать класс, который принимает определения схемы в качестве аргумента, и проверить весь объект в целом:

interface Node<T extends string = string> {
    name: T;
}


type Scalars = 'Int' | 'Boolean' | 'String'

type AllTypes<T extends Record<keyof T, any>> = keyof T | Scalars
type Values<T> = T[keyof T]

type SchemNodes<T extends Record<keyof T, Record<string, AllTypes<T>>>> =
    { [P in Extract<keyof T, string>]: Node<P> }

class Schema<T extends Record<keyof T, Record<string, AllTypes<T>>>> {
    constructor(types: T) { }
    getNodes(): SchemNodes<T> {
        return null!;
    }
    renderTypes(types: Array<Values<SchemNodes<T>>>) { }
}

const s = new Schema({
    "User": { posts: "Post" },
    "Blog": { users: "User" },
    // "Comment": {},
    "Post": { title: "String", comments: 'Comment' }
})

const { Blog, Post, User } = s.getNodes();

Play

...