TypeScript,JavaScript:如何通过循环typeof动态创建组合类型?

时间:2018-07-24 14:40:18

标签: javascript typescript

假设我们有

class ExpandableView: RelativeLayout {

    var expandAbleHeight = 0

    constructor(context: Context) : super(context) {

    }

    init {

        val rlp = RelativeLayout.LayoutParams(
                RelativeLayout.LayoutParams.MATCH_PARENT,
                RelativeLayout.LayoutParams.WRAP_CONTENT)
        rlp.marginStart = 12
        rlp.marginEnd = 12

        val tv = TextView(context)
        tv.text = "sample.sample.sample.sample.sample.sample.sample.sample.sample.sample.sample.sample.sample.sample.sample.sample.sample.sample.\n" +
            "            sample.sample.sample.sample.sample.sample.sample.sample.sample.sample.sample.sample.sample.sample.sample.sample.sample.sample.sample.sample.\n" +
            "            sample.sample.sample.sample.sample.sample.sample.sample.sample.sample.sample.sample.sample.sample.sample.sample.sample.sample.sample.sample."

        val lp = RelativeLayout.LayoutParams(
                RelativeLayout.LayoutParams.WRAP_CONTENT,
                RelativeLayout.LayoutParams.WRAP_CONTENT)
        lp.addRule(RelativeLayout.CENTER_IN_PARENT)
        lp.marginEnd = 8
        lp.marginStart = 8

        tv.layoutParams = lp

        this.addView(tv)
    }

    override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) {
        val tv = TextView(context)
        tv.measure(MeasureSpec.makeMeasureSpec(tv.measuredWidth, MeasureSpec.AT_MOST), MeasureSpec.makeMeasureSpec(tv.measuredHeight, MeasureSpec.AT_MOST))
        tv.layout(8,8,8,8)
    }

    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
        expandAbleHeight = heightMeasureSpec
    }

    fun getExpandHeight(): Int {
        return expandAbleHeight
    }
}

使得结果等于

const array = [a, b, c]  // could be any number of items in here
type T = ???  // how to write this?

除了 T 是基于遍历 array 的动态创建的(因为 array 可能实际上包含 a b c )。使用接口的解决方案也是可以接受的。

3 个答案:

答案 0 :(得分:2)

如果正确键入数组,它将被键入为元素类型的并集。例如:

let a = { aProp: 1 };
let b = { bProp: 1 };
let c = { cProp: 1 };
const array = [a, b, c]  // typed as ({ aProp: number; } | { bProp: number; } | { cProp: number; })[]

从此开始,我们可以使用条件类型将并集转换为相交(有关UnionToIntersection的说明,请参见此answer),并使用类型查询来获取数组中项的类型:

type UnionToIntersection<U> = 
    (U extends any ? (k: U) => void : never) extends ((k: infer I) => void) ? I : never

type T = UnionToIntersection<typeof array[number]>  // { aProp: number; } & { bProp: number; } & { cProp: number; }

答案 1 :(得分:0)

Typescript类型仅在编译时存在,它们将在运行时被删除(使其变为javascript)。由于数组的值仅在运行时存在,因此无法在编译时获取其类型。

答案 2 :(得分:0)

好吧,所以我无法获得@Titian的答案;这是另一个解决方案

type Intersect<T extends any[]> = {[key in keyof T[number]] :T[number][key]};

您可以这样使用它,例如 enter image description here 看看IDE如何知道其属性,如果尝试分配其他任何内容,就会出错!


不幸的是,它认为它们是可选的,如果您强迫它们全部都是真实的,则可以解决此问题。

type Defined<T> = T extends (undefined|void) ? never : T;
type Intersect<T extends any[]> = {[key in keyof T[number]] :Defined<T[number][key]>};

这里的问题是,如果您的类型是{optional ?:string},我想您会失去细微差别。


我认为简单的&可以解决此问题,但不能解决(并且为“动态”数组的大小增加了上限;在这种情况下,我只能为复制粘贴而烦恼)用于6个长度的数组。

type Intersect<T extends any[]> =
    T[5] extends void ? T[4] extends void ? T[3] extends void ? T[2] extends void ? T[1] extends void ? T[0] extends void ?
    [] : T[0] : T[0]&T[1] : T[0]&T[1]&T[2] : T[0]&T[1]&T[2]&T[3] : T[0]&T[1]&T[2]&T[3]&T[4] : T[0]&T[1]&T[2]&T[3]&T[4]&T[5]
;

我觉得真正的解决方案是当figurerecursive类型时。

type Head<T> = T extends [infer U, ...Array<unknown>] ? U : never;
type Tail<T> = T extends any[] ?
    (...args :T) => any extends (head :any, ...args :infer U) => any ?
        U :
        never
    :
    never
;
type Intersect<T extends any[]> = T[1] extends void ? T[0] : Head<T>&Intersect<Tail<T>>;

HeadTail(可以分别提取数组的第一种类型,其余类型分别提取为新的数组类型)都可以,但是Intersect在返回时会中断到Intersect