有熟悉 typescript 的前端大佬吗, 问一个关于类型推断的问题

2019-12-05 10:58:00 +08:00
 kkopitehong
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, 返回一个对象, 这个要怎么写类型声明, 让返回的对象能推断出其有tomjack这两个属性

之前用到inquirer, 就有这种推断, 但是看其声明文件看的云里雾里的,照着写好像也不大对

gif 地址 (编辑时预览的 gif 貌似没有动, 直接传图片)

3461 次点击
所在节点    JavaScript
13 条回复
BBCCBB
2019-12-05 11:05:53 +08:00
看过 typescript, 但没怎么写过, 我瞎说一下 ==.

interface A {
tom: any
jack: any
}


function xxx(): A {}
BBCCBB
2019-12-05 11:06:52 +08:00
但这样写 transform 方法目测又要报错了
kkopitehong
2019-12-05 11:09:32 +08:00
@BBCCBB 不能直接这样声明返回类型嗯 因为传入的每一个对象的`name`属性值都是不确定的
hyyou2010
2019-12-05 11:32:24 +08:00
好久没看了,水平也有限,猜是类似这样的东西:

interface A {
[key:string]: any
[propName:string]: any
[index:string|number]:any
}
hyyou2010
2019-12-05 11:35:09 +08:00
另外,如果只有 tom 和 jack 这两种可能的话,应该用枚举。同时函数的输入参数也应该用枚举限制为只有 tom 和 jack
kkopitehong
2019-12-05 11:45:25 +08:00
@hyyou2010 这个是不定的, 比方我传入[{name: 'foo'}, {name: 'bar'}], 那返回的就应该是{ foo: {}, bar: {} }
kkopitehong
2019-12-05 11:48:10 +08:00
@hyyou2010 你可以装下 inquirer 看看效果, 尝试修改下传入对象的 name
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"]); // ✔️
```
likoshow
2019-12-05 12:41:55 +08:00
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" }
]);
keelii
2019-12-05 13:16:59 +08:00
tom 和 jack 这两个属性不是类型,是运行时的字符串变量。你想把它推断出来只能手动写上 String Literal Type
miaowing
2019-12-05 13:29:06 +08:00
@hyyou2010 同感觉是这个
kkopitehong
2019-12-05 13:35:44 +08:00
@likoshow 感谢
kkopitehong
2019-12-05 13:39:50 +08:00
@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>
}
```

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/626139

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX