interface Foo {
name: string
type: string
}
function transform(arr: Foo[]) {
const obj = {}
arr.forEach(item => {
obj[item.name] = {
...
}
})
return obj
}
transform([
{ name: 'tom', type: 'confirm' },
{ name: 'jack', type: 'confirm' }
])
// { tom: {}, jack: {} }
transform
方法将传入的数组中每一个对象的name
属性值作为 key, 返回一个对象, 这个要怎么写类型声明, 让返回的对象能推断出其有tom
和jack
这两个属性
之前用到inquirer
, 就有这种推断, 但是看其声明文件看的云里雾里的,照着写好像也不大对
gif 地址 (编辑时预览的 gif 貌似没有动, 直接传图片)
1
BBCCBB 2019-12-05 11:05:53 +08:00
看过 typescript, 但没怎么写过, 我瞎说一下 ==.
interface A { tom: any jack: any } function xxx(): A {} |
2
BBCCBB 2019-12-05 11:06:52 +08:00
但这样写 transform 方法目测又要报错了
|
3
kkopitehong OP @BBCCBB 不能直接这样声明返回类型嗯 因为传入的每一个对象的`name`属性值都是不确定的
|
4
hyyou2010 2019-12-05 11:32:24 +08:00
好久没看了,水平也有限,猜是类似这样的东西:
interface A { [key:string]: any [propName:string]: any [index:string|number]:any } |
5
hyyou2010 2019-12-05 11:35:09 +08:00
另外,如果只有 tom 和 jack 这两种可能的话,应该用枚举。同时函数的输入参数也应该用枚举限制为只有 tom 和 jack
|
6
kkopitehong OP @hyyou2010 这个是不定的, 比方我传入[{name: 'foo'}, {name: 'bar'}], 那返回的就应该是{ foo: {}, bar: {} }
|
7
kkopitehong OP @hyyou2010 你可以装下 inquirer 看看效果, 尝试修改下传入对象的 name
|
8
yuanfnadi 2019-12-05 11:51:39 +08:00
```
interface PathArray<T, L> extends Array<string | number> { ["0"]?: keyof T; ["1"]?: L extends { ["0"]: infer K0; } ? K0 extends keyof T ? keyof T[K0] : never : never; ["2"]?: L extends { ["0"]: infer K0; ["1"]: infer K1; } ? K0 extends keyof T ? K1 extends keyof T[K0] ? keyof T[K0][K1] : never : never : never; ["3"]?: L extends { ["0"]: infer K0; ["1"]: infer K1; ["2"]: infer K2; } ? K0 extends keyof T ? K1 extends keyof T[K0] ? K2 extends keyof T[K0][K1] ? keyof T[K0][K1][K2] : never : never : never : never; ["4"]?: L extends { ["0"]: infer K0; ["1"]: infer K1; ["2"]: infer K2; ["3"]: infer K3; } ? K0 extends keyof T ? K1 extends keyof T[K0] ? K2 extends keyof T[K0][K1] ? K3 extends keyof T[K0][K1][K2] ? keyof T[K0][K1][K2][K3] : never : never : never : never : never; ["5"]?: L extends { ["0"]: infer K0; ["1"]: infer K1; ["2"]: infer K2; ["3"]: infer K3; ["4"]: infer K4; } ? K0 extends keyof T ? K1 extends keyof T[K0] ? K2 extends keyof T[K0][K1] ? K3 extends keyof T[K0][K1][K2] ? K4 extends keyof T[K0][K1][K2][K3] ? keyof T[K0][K1][K2][K3][K4] : never : never : never : never : never : never; ["6"]?: L extends { ["0"]: infer K0; ["1"]: infer K1; ["2"]: infer K2; ["3"]: infer K3; ["4"]: infer K4; ["5"]: infer K5; } ? K0 extends keyof T ? K1 extends keyof T[K0] ? K2 extends keyof T[K0][K1] ? K3 extends keyof T[K0][K1][K2] ? K4 extends keyof T[K0][K1][K2][K3] ? K5 extends keyof T[K0][K1][K2][K3][K4] ? keyof T[K0][K1][K2][K3][K4][K5] : never : never : never : never : never : never : never; } type ArrayHasIndex<MinLenght extends number> = { [K in MinLenght]: any }; export type PathArrayValue< T, L extends PathArray<T, L> > = L extends ArrayHasIndex<0 | 1 | 2 | 3 | 4 | 5 | 6 | 7> ? any : L extends ArrayHasIndex<0 | 1 | 2 | 3 | 4 | 5 | 6> ? T[L[0]][L[1]][L[2]][L[3]][L[4]][L[5]][L[6]] : L extends ArrayHasIndex<0 | 1 | 2 | 3 | 4 | 5> ? T[L[0]][L[1]][L[2]][L[3]][L[4]][L[5]] : L extends ArrayHasIndex<0 | 1 | 2 | 3 | 4> ? T[L[0]][L[1]][L[2]][L[3]][L[4]] : L extends ArrayHasIndex<0 | 1 | 2 | 3> ? T[L[0]][L[1]][L[2]][L[3]] : L extends ArrayHasIndex<0 | 1 | 2> ? T[L[0]][L[1]][L[2]] : L extends ArrayHasIndex<0 | 1> ? T[L[0]][L[1]] : L extends ArrayHasIndex<0> ? T[L[0]] : never; export type Path<T, L> = PathArray<T, L> | keyof T; export type PathValue<T, L extends Path<T, L>> = L extends PathArray<T, L> ? PathArrayValue<T, L> : L extends keyof T ? T[L] : any; declare function path<T, L extends Path<T, L>>( object: T, params: L ): PathValue<T, L>; const obj = { v: { w: { x: { y: { z: { a: { b: { c: 2 } } } } } }, ouch: true, foo: [{ bar: 2 }, { bar: 3 }] } }; const output: number = path(obj, ["v", "w", "x"]); // 💥 const output2: object = path(obj, ["v", "w", "x"]); // ✔️ const output4: { c: string } = path(obj, ["v", "w", "x", "y", "z", "a", "b"]); // 💥 const output3: { c: number } = path(obj, ["v", "w", "x", "y", "z", "a", "b"]); // ✔️ const output5: "wrong" = path(obj, ["v", "w", "x", "y", "z", "a", "b", "c"]); // ✔️ since after 7 levels there is no typechecking const x = path(obj, ["v", "ouch", "x"]); // 💥 const y = path(obj, ["v", "ouch", "y"]); // 💥 const z = path(obj, ["v", "ouch", "somethingCompletelyDifferent"]); // 💥 path(obj, "!"); // 💥 path(obj, ["!"]); // 💥 path(obj, ["v", "!"]); // 💥 path(obj, ["v", "w", "!"]); // 💥 path(obj, ["v", "w", "x", "!"]); // 💥 path(obj, ["v", "w", "x", "y", "!"]); // 💥 path(obj, ["v", "w", "x", "y", "z", "!"]); // 💥 path(obj, ["v", "w", "x", "y", "z", "a", "!"]); // 💥 path(obj, ["v", "w", "x", "y", "z", "a", "b", "!"]); // ✔️ since after 7 levels there is no typechecking path(obj, "v"); // ✔️ path(obj, ["v"]); // ✔️ path(obj, ["v", "w"]); // ✔️ path(obj, ["v", "w", "x"]); // ✔️ path(obj, ["v", "w", "x", "y"]); // ✔️ path(obj, ["v", "w", "x", "y", "z"]); // ✔️ path(obj, ["v", "w", "x", "y", "z", "a"]); // ✔️ path(obj, ["v", "w", "x", "y", "z", "a", "b"]); // ✔️ path(obj, ["v", "w", "x", "y", "z", "a", "b", "c"]); // ✔️ ``` |
9
likoshow 2019-12-05 12:41:55 +08:00 3
type ReturnObj<T extends string> = {
[key in T]: any; }; interface Foo { name: string; type: string; } function transform<T extends Foo['name']>(arr: { name: T, type: string }[]) { const obj = {} as ReturnObj<T> arr.forEach(item => { obj[item.name] = { // ... }; }); return obj as ReturnObj<T> } const result = transform([ { name: "tom", type: "confirm" }, { name: "jack", type: "confirm" } ]); |
10
keelii 2019-12-05 13:16:59 +08:00
tom 和 jack 这两个属性不是类型,是运行时的字符串变量。你想把它推断出来只能手动写上 String Literal Type
|
12
kkopitehong OP @likoshow 感谢
|
13
kkopitehong OP @likoshow 稍做下修改
``` type ReturnObj<T extends string> = { [key in T]: any; }; interface Foo<T extends string> { name: T; type: string; } function transform<T extends string>(arr: Foo<T>[]) { const obj = {} as ReturnObj<T> arr.forEach(item => { obj[item.name] = { // ... }; }); return obj as ReturnObj<T> } ``` |