首页   注册   登录
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐关注
Meteor
JSLint - a JavaScript code quality tool
jsFiddle
D3.js
WebStorm
推荐书目
JavaScript 权威指南第 5 版
Closure: The Definitive Guide
kkopitehong
V2EX  ›  JavaScript

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

  •  1
     
  •   kkopitehong · 43 天前 · 1259 次点击
    这是一个创建于 43 天前的主题,其中的信息可能已经有所发展或是发生改变。
    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, 就有这种推断, 但是看其声明文件看的云里雾里的,照着写好像也不大对

    Q3wH4x.md.gif

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

    13 回复  |  直到 2019-12-05 13:39:50 +08:00
    BBCCBB
        1
    BBCCBB   43 天前
    看过 typescript, 但没怎么写过, 我瞎说一下 ==.

    interface A {
    tom: any
    jack: any
    }


    function xxx(): A {}
    BBCCBB
        2
    BBCCBB   43 天前
    但这样写 transform 方法目测又要报错了
    kkopitehong
        3
    kkopitehong   43 天前
    @BBCCBB 不能直接这样声明返回类型嗯 因为传入的每一个对象的`name`属性值都是不确定的
    hyyou2010
        4
    hyyou2010   43 天前
    好久没看了,水平也有限,猜是类似这样的东西:

    interface A {
    [key:string]: any
    [propName:string]: any
    [index:string|number]:any
    }
    hyyou2010
        5
    hyyou2010   43 天前
    另外,如果只有 tom 和 jack 这两种可能的话,应该用枚举。同时函数的输入参数也应该用枚举限制为只有 tom 和 jack
    kkopitehong
        6
    kkopitehong   43 天前
    @hyyou2010 这个是不定的, 比方我传入[{name: 'foo'}, {name: 'bar'}], 那返回的就应该是{ foo: {}, bar: {} }
    kkopitehong
        7
    kkopitehong   43 天前
    @hyyou2010 你可以装下 inquirer 看看效果, 尝试修改下传入对象的 name
    yuanfnadi
        8
    yuanfnadi   43 天前
    ```
    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
        9
    likoshow   43 天前   ♥ 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" }
    ]);
    keelii
        10
    keelii   43 天前
    tom 和 jack 这两个属性不是类型,是运行时的字符串变量。你想把它推断出来只能手动写上 String Literal Type
    miaowing
        11
    miaowing   43 天前
    @hyyou2010 同感觉是这个
    kkopitehong
        12
    kkopitehong   43 天前
    @likoshow 感谢
    kkopitehong
        13
    kkopitehong   43 天前   ♥ 1
    @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>
    }
    ```
    关于   ·   FAQ   ·   API   ·   我们的愿景   ·   广告投放   ·   感谢   ·   实用小工具   ·   1534 人在线   最高记录 5168   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.3 · 24ms · UTC 17:28 · PVG 01:28 · LAX 09:28 · JFK 12:28
    ♥ Do have faith in what you're doing.