V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
wednesdayco
V2EX  ›  TypeScript

这种的类型是不是实现不了(TypeScript)

  •  1
     
  •   wednesdayco · 329 天前 · 2311 次点击
    这是一个创建于 329 天前的主题,其中的信息可能已经有所发展或是发生改变。
    需求: 一个类型必须包含 id 属性,值的类型为 string ,同时它可能包含其他的属性,但值的类型必须是 number 。
    第 1 条附言  ·  329 天前
    其实只提示类型的话,大家给出了答案。问题出在初始化上:

    需求场景也很简单,我有一个列表,这个列表每一行有一个 id ,是 string 的。
    但是其他的属性都是 number 。
    const data: Item = {
    id: 'xxx-xxx-xxx', // id 只能是 string
    cid: 123123123,
    name: 233233,
    ...//任意属性,但值必须是 number 不能是其他的或者 string
    }
    第 2 条附言  ·  329 天前
    折中玩法

    const id = Symbol('id');
    type CustomType = {
    [t: string]: number
    [id: symbol]: string
    }

    const data:CustomType = {
    [id]: '123123xxx123',
    v2: 123233
    }
    32 条回复    2024-03-13 05:02:40 +08:00
    nomagick
        1
    nomagick  
       329 天前   ❤️ 1
    ```typescript
    interface wtf {
    id: string;
    [k: string]: number;
    }

    ```
    liahu
        2
    liahu  
       329 天前
    {
    id: string
    [key: string]: any
    }
    ???
    simoger
        3
    simoger  
       329 天前
    interface MyObject {
    id: string;
    [key: string]: number | string;
    }
    这种?
    lilei2023
        4
    lilei2023  
       329 天前
    这不是有三位热心网友帮你实现了一个么?
    retrocode
        5
    retrocode  
       329 天前
    这种?

    ```ts
    interface Obj {
    id: string
    }
    interface Somebody extends Obj {
    key?: number
    }
    ```
    wednesdayco
        6
    wednesdayco  
    OP
       329 天前
    @lilei2023 上面三位都不对
    retrocode
        7
    retrocode  
       329 天前
    明白你意思了, 我理解问题, 1 楼是对的
    retrocode
        8
    retrocode  
       329 天前
    你不妨 给个具体示例, 看你回复上面都不对完全不理解什么意思了
    wednesdayco
        9
    wednesdayco  
    OP
       329 天前
    @retrocode 1 楼也不对
    Property 'id' of type 'string' is not assignable to 'string' index type 'number'.ts(2411)
    wednesdayco
        10
    wednesdayco  
    OP
       329 天前
    @liahu 其他的属性值的类型必须是 number ,除了属性 id 的值的类型必须是 string
    wednesdayco
        11
    wednesdayco  
    OP
       329 天前
    @simoger 除属性 id 的值的类型是 string 之外,其他的值的类型都必须是 number
    MRG0
        12
    MRG0  
       329 天前
    放一个数据样例更好解决问题
    tearzx
        13
    tearzx  
       329 天前
    interface A{
    [key: string]: number;
    }

    interface B {
    id: string;
    }

    type C = A | B;
    nomagick
        14
    nomagick  
       329 天前
    还真是,等一个答案
    wednesdayco
        15
    wednesdayco  
    OP
       329 天前
    @tearzx 不能限制 id 的类型,这样 id 可以为 number
    wednesdayco
        16
    wednesdayco  
    OP
       329 天前
    @MRG0

    const data: IData = {
    id: 'xxx-xxx-xxx', // id 只能是 string
    cid: 123123123,
    name: 233233,
    ...//任意属性,但值必须是 number 不能是其他的或者 string
    }
    Pencillll
        17
    Pencillll  
       329 天前 via Android
    实现不了,因为没有办法把 'id' 从 string 里面排除出来
    fannheyward
        18
    fannheyward  
       329 天前
    #3 是对的

    ```
    interface IData {
    id: string
    [k: string]: number | string
    }
    ```
    fannheyward
        19
    fannheyward  
       329 天前   ❤️ 1
    @fannheyward 不对,这样其他 key 可以是 string 了
    rrfeng
        20
    rrfeng  
       329 天前
    string indexer MUST accommodate all members
    nomagick
        21
    nomagick  
       329 天前   ❤️ 1
    我解开了


    interface NumO {
    [k: string]: number;
    }

    type WithID<T> = {
    id: string;
    } & T;
    type WTF = WithID<NumO>;

    const v = {
    id: 'wtf'
    } as WTF;

    // OK
    v.id = 'str';
    v.a = 3;
    v.b = 4;
    // Not OK
    v.c = 'sr';
    gam2046
        22
    gam2046  
       329 天前   ❤️ 1
    interface A {
    id: string;
    }

    interface B {
    [k: string]: number
    }

    type AB = A & B

    function sample(arg: AB) {
    arg.id = 'aaa'
    arg.bcd = 1234
    }
    wednesdayco
        23
    wednesdayco  
    OP
       329 天前
    @nomagick 这样做在类型提示上确实是对的,问题在初始化的时候不能加除了 id 以外的属性
    nomagick
        24
    nomagick  
       329 天前
    再加一个类型参数,现场合并。。。
    真拧巴。。。
    rrfeng
        25
    rrfeng  
       329 天前   ❤️ 1
    @gam2046 这样实际上 AB 不存在……,所以是 bug 。
    NoManPlay
        26
    NoManPlay  
       329 天前   ❤️ 1
    ```
    type CustomType = {
    [key: string]: number | string;
    } & {
    id: string;
    };

    // 通过映射类型和条件类型来移除 id 属性外的 string 类型
    type StrictCustomType = {
    [K in keyof CustomType]: CustomType[K] extends string ? (K extends 'id' ? string : never) : number;
    };
    ```
    wednesdayco
        27
    wednesdayco  
    OP
       329 天前
    @NoManPlay 想法很好,但是实际初始化 StrictCustomType 的对象的时候,会报错。
    Pencillll
        28
    Pencillll  
       329 天前
    对于初始化来说,如果可以加个 helper 函数的话,倒是有一种办法:

    type CoercedItem<T> = {
    [K in keyof T]: K extends 'id' ? string : number
    }

    type ValidItem<T> = T extends CoercedItem<T> ? 'id' extends keyof T ? T : never : never

    function makeItem<T>(item: ValidItem<T>): T {
    return item
    }

    const a = makeItem({ id: 'x', no: 1 })

    a.id = 'y'
    a.no = 2

    const b = makeItem({ id: 'z' })

    // 报错
    const c = makeItem({ id: 1, no: 1 })

    // 报错
    const d = makeItem({ no: 1 })
    Pencillll
        29
    Pencillll  
       329 天前
    不过上面这样有个缺陷是初始化之后的对象不能再添加新的属性
    CLMan
        30
    CLMan  
       329 天前
    好奇什么场景需要这么复杂的类型定义呢?

    或者说类似问题的类型约束的严谨性和代码简洁应该如何取舍?

    我个人是把 TS 当作一个不太完美的类型约束工具,更倾向于代码简洁性。
    Gaoti
        31
    Gaoti  
       327 天前
    ```ts
    type Others = Record<string, number>;

    type T<O = Others> = {
    id: string;
    } & {
    [K in keyof O]: K extends "id" ? string : number;
    };

    const obj: T<{ id: string; aaa: number }> = {
    id: "1",
    aaa: 123,
    bbb: 1, // error
    };
    ```

    使用的时候泛型传入对应的类型就行,但是和 #28 说的一样,泛型中没有定义的的字段会报错
    chnwillliu
        32
    chnwillliu  
       286 天前 via Android
    const id = ()=> ('i' + 'd ').trim();

    const n:number = obj[id()];

    如果有这样的类型你这样操作那 TS 该不该报错? key 是运行时决定,所以有可能是 string 也有可能是 number 。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1056 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 28ms · UTC 20:04 · PVG 04:04 · LAX 12:04 · JFK 15:04
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.