【问题标题】:TypeScript feild's type depend on another field's typeTypeScript 字段的类型取决于另一个字段的类型
【发布时间】:2021-09-04 21:52:47
【问题描述】:

我想用 TypeScript 写一个解释器。这种语言有两种基本类型,我将它们定义如下。

type DataType = { kind: 'number' } | {kind: 'string' }

然后我想定义另一种类型,也包括它的实例。

type _DataType<T extends 'type' | 'typedValue'> = {
    kind: 'number',
    value: T extends 'type' ? undefined : number,
} | {
    kind: 'string'
    value: T extends 'type' ? undefined : string,
}
type DataType = _DataType<'type'>;
type TypedValue = _DataType<'typedValue'>;

效果很好。但是当我想定义一个像数组这样的高阶类型时,它可以将原始DataType 作为参数。我不知道如何定义它。以下是我尝试过的:

type _HigherType<T extends 'type' | 'typedValue'> = {
    kind: 'Array',
    t: DataType,
    value: Array<?????>
} | {
    kind: 'Single',
    t: DataType,
    value: ?????
}
type HigherType = _HigherType<'type'>;
type HigherTypedValue = _HigherType<'typedValue'>;

貌似value的类型依赖于t的类型,但是如何表达它们的关系呢?

也许我可以像下面这样扩展HigherType

type _HigherType<T extends 'type' | 'typedValue'> = {
    kind: 'Array',
    t: { kind: 'number },
    value: T extends 'type' ? undefined : Array<number>
} | {
    kind: 'Array',
    t: { kind: 'string' },
    value: T extends 'type' ? undefined : Array<string>
} | {
    kind: 'single',
    t: { kind: 'number' },
    value: T extends 'type' ? undefined : number
} | {
    kind: 'single',
    t: { kind: 'string' },
    value: T extends 'type' ? undefined : string
}

但是如果有m个DataType变体和n个HigherType(尽管有Array,我们可能有Option, Set,......),我们需要写m*n个变体,而且大部分都有重复的部分。有没有办法只定义 m + n 个变体?

【问题讨论】:

    标签: typescript typescript-typings typescript-generics


    【解决方案1】:

    如果你需要在打字稿系统中表达m*n,你应该使用分布性。

    考虑一个简单的例子:

    type All = 'a' | 'b' | 'c'
    
    type Variants<T extends string> = T extends any ? { tag: T } : any
    
    
    type Result_ = Variants<All> // union of all possible states
    
    // WIthout distribution
    type Variants2<T> = { tag: T }
    
    type Result__=Variants2<All> // no union
    

    您还可以找到this articlethis question 有帮助。

    解决方案:

    
    type Kinds = 'number' | 'string'
    type Value = 'type' | 'typedValue'
    
    /**
     * HashMap for type names
     */
    type Dictionary = {
        number: number,
        string: string,
    }
    
    type HandleValue<V extends Value, Type> = V extends 'type' ? undefined : Type
    
    type Values<T> = T[keyof T]
    
    type DataType_<V extends Value> = Values<{
        [Prop in Kinds]: {
            kind: Prop,
            value: HandleValue<V, Dictionary[Prop]>
        }
    }>
    
    
    type DataType = DataType_<'type'>;
    type TypedValue = DataType_<'typedValue'>;
    
    type HigherTypes = 'Array' | 'single' //| 'Option' | 'Set'
    
    /**
     * You should puth in this conditional type all allowed tpye constructors:
     * Array, Set, Options ....
     */
    type MapStructure<Prop extends string, T extends DataType> =
        (Prop extends 'Array'
            ? Array<Dictionary[T['kind']]>
            : (Prop extends 'single'
                ? Dictionary[T['kind']]
                : never
            )
        )
    
    type HigherOrderType<V extends Value, T extends DataType = DataType> =
        Values<{
            [Prop in HigherTypes]: (T extends any
                ? {
                    kind: Prop,
                    t: Pick<T, 'kind'>,
                    value: HandleValue<V, MapStructure<Prop, T>>
                }
                : never)
        }>
    
    type Result = HigherOrderType<'type'>
    
    type _HigherType<T extends 'type' | 'typedValue'> = {
        kind: 'Array',
        t: { kind: 'number' },
        value: T extends 'type' ? undefined : Array<number>
    } | {
        kind: 'Array',
        t: { kind: 'string' },
        value: T extends 'type' ? undefined : Array<string>
    } | {
        kind: 'single',
        t: { kind: 'number' },
        value: T extends 'type' ? undefined : number
    } | {
        kind: 'single',
        t: { kind: 'string' },
        value: T extends 'type' ? undefined : string
    }
    
    
    /**
     * Tests
     */
    type HigherType = _HigherType<'type'> extends HigherOrderType<'type'> ? true : false; // true
    type HigherTypedValue = _HigherType<'typedValue'> extends HigherOrderType<'typedValue'> ? true : false; // true
    

    Playground 如果你想分发smth,就用T extends any

    【讨论】:

      猜你喜欢
      • 2021-09-21
      • 2020-06-03
      • 2022-01-14
      • 1970-01-01
      • 2021-10-12
      • 2017-10-07
      • 1970-01-01
      • 2023-02-15
      • 2022-01-19
      相关资源
      最近更新 更多