简体   繁体   English

通过 static 属性过滤泛型类型的实用程序类型

[英]Utility type to filter generic types by a static property

I would like to filter a specific generic type according to a static property.我想根据 static 属性过滤特定的泛型类型。 It's easier to show than to explain.展示比解释更容易。 I am happy to rephrase the question if anyone can give me a hint how to describe it better (things are quite abstract for a TS novice like me).如果有人可以给我提示如何更好地描述它,我很高兴重新表述这个问题(对于像我这样的 TS 新手来说,事情非常抽象)。

// My given types
interface ChardataNode {
    type: "Chardata"
    value: string
}
interface ElementNode<TName extends string = string, TChild extends Child = Child> {
    type: "Element"
    name: TName
    children: TChild[]
}
type Child = ElementNode | ChardataNode

// This is what I tried. Unfortunately, the first utility type does not work at all as it doesn't filter out the element children 
type ElementChildren<TParent> = TParent extends ElementNode ? TParent["children"][0]["type"] extends "Element" ? TParent["children"] : never : never
type ElementNames<TElements> = TElements extends ElementNode[] ? TElements[0]["name"] : never

// Setup an example type for testing
type X = ElementNode<"Root", ChardataNode | ElementNode<"Child1", never> | ElementNode<"Child2", never>>

// What I want is: "Child1" | "Child2", but what I get is: never
type Names = ElementNames<ElementChildren<X>>

Typescript Playground Typescript 游乐场

The part with the array was confusing the compiler.带有数组的部分使编译器感到困惑。
You have to have a separate step to check for array and a separate step for the array element.您必须有一个单独的步骤来检查数组和一个单独的步骤来检查数组元素。

interface ChardataNode {
    type: 'Chardata'
    value: string
}
interface ElementNode<TName extends string = string, TChild extends Child = Child> {
    type: 'Element'
    name: TName
    children: TChild[]
}
type Child = ElementNode | ChardataNode

// It's not necessary to check the type of children if you're only interested in the name
type ElementChildren<TParent> = TParent extends ElementNode ? TParent['children'] : never

type ElementName<TElement> = TElement extends ElementNode ? TElement['name'] : never
type ElementsName<TElements> = TElements extends any[] ? ElementName<TElements[0]> : never


// Set up an example type for testing
type X = ElementNode<'Root', ChardataNode | ElementNode<'Child1', never> | ElementNode<'Child2', never>>

// XChildren is an array of ChardataNode | ElementNode<'Child1', never> | ElementNode<'Child2', never>
// but that's not a problem for extracting the name
type XChildren = ElementChildren<X>
// XChildName is 'Child1' | 'Child2'
type XChildName = ElementsName<XChildren>

// Alternative if you're not working with arrays
type XChild = ElementChildren<X>[0]
type XChildNameAlt = ElementName<XChild>


// Utility in case you're looking to extract just children of a certain type
type ElementChildOfType<TParent, TChild> = Extract<ElementChildren<TParent>[0], TChild>
type ElementChildOfTypeElementNode<TParent> = ElementChildOfType<TParent, ElementNode>

// XChildElement is ElementNode<'Child1', never> | ElementNode<'Child2', never>
type XChildElement = ElementChildOfTypeElementNode<X>

Tested with v4.7.4使用 v4.7.4 测试

Edit:编辑:
You had an unnecessary condition in ElementChildren, which was not necessary for extracting the names.您在 ElementChildren 中有一个不必要的条件,这对于提取名称不是必需的。
I've created a separate type for extracting elements of a certain type, if you specifically need that.如果您特别需要,我创建了一个单独的类型来提取某种类型的元素。

In the end, I came up with this solution:最后,我想出了这个解决方案:

// My given types
interface ChardataNode {
    type: "Chardata"
    value: string
}
interface ElementNode<TName extends string = string, TChild extends Child = Child> {
    type: "Element"
    name: TName
    children: TChild[]
}
type Child = ElementNode | ChardataNode

// The fixed utility types 
type ElementChildren<TParent> = TParent extends ElementNode ? Extract<TParent["children"][0], ElementNode> : never
type ElementNames<TElement> = TElement extends ElementNode ? TElement["name"] : never

// Setup an example type for testing
type X = ElementNode<"Root", ChardataNode | ElementNode<"Child1", never> | ElementNode<"Child2", never>>

// Results in "Child1" | "Child2"
type Names = ElementNames<ElementChildren<X>>

Typescript Playground Typescript 游乐场

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

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