假设我们有
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 )。使用接口的解决方案也是可以接受的。
答案 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]};
您可以这样使用它,例如 看看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]
;
我觉得真正的解决方案是当figure种recursive类型时。
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>>;
Head
和Tail
(可以分别提取数组的第一种类型,其余类型分别提取为新的数组类型)都可以,但是Intersect
在返回时会中断到Intersect
。