简体   繁体   English

使用一个 function 的推断通用参数类型来键入其他函数

[英]Use inferred generic param type of one function to type other functions

I'm building an API to render a schema graph.我正在构建一个 API 来呈现模式图。

Here's what it looks like (ultra-simplified)这是它的样子(超简化)

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]);

I would like to make sure that one cannot reference a type that wasn't registered in the Schema.render from the Schema.createNode function.我想确保不能从Schema.createNode function 引用未在Schema.render中注册的类型。

In the example above, fields should basically be of type: Record<string, Scalars | 'User' | 'Post' | 'Blog'在上面的示例中, fields基本上应该是以下类型: Record<string, Scalars | 'User' | 'Post' | 'Blog' Record<string, Scalars | 'User' | 'Post' | 'Blog' Record<string, Scalars | 'User' | 'Post' | 'Blog' , where User | Post | Blog Record<string, Scalars | 'User' | 'Post' | 'Blog' ,其中User | Post | Blog User | Post | Blog User | Post | Blog is inferred from the nodes passed to s.render从传递给s.render的节点推断User | Post | Blog

To do that, I'd like to infer the names of the nodes passed to the render function, in order to type the values of the param fields of Schema.createNode .为此,我想推断传递给render function 的节点的名称,以便键入Schema.createNode的参数fields的值。

Unfortunately, generics are only captured by function parameters if declared on a function level, and not if declared on an interface level (which makes sense).不幸的是,如果在 function 级别上声明,generics 仅由 function 参数捕获,如果在接口级别上声明则不会捕获(这是有道理的)。

How can I reuse the inferred generic of s.render<T extends Node[]>(nodes: T) to type the function createNode(name: string, fields: AllTypes<T>) where T is the same inferred type shared between the two functions?如何重用s.render<T extends Node[]>(nodes: T)的推断泛型来键入 function createNode(name: string, fields: AllTypes<T>)其中T是在两个功能?

Is there a way to make this work?有没有办法使这项工作? (even with a slightly different API) (即使 API 略有不同)

Thanks谢谢

I don't think you can get this to work with multiple calls.我不认为你可以让它与多个电话一起工作。 The solution that woks best would be to create a class that takes the schema definitions as an argument and check the whole object as a whole:最好的解决方案是创建一个 class ,它将模式定义作为参数并检查整个 object 作为一个整体:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM