怎么每次碰到想不通的代码行都出在 TypeScript 这行到底哪里有问题? - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
june4
V2EX    TypeScript

怎么每次碰到想不通的代码行都出在 TypeScript 这行到底哪里有问题?

  •  1
     
  •   june4 2024-09-02 10:28:03 +08:00 3348 次点击
    这是一个创建于 409 天前的主题,其中的信息可能已经有所发展或是发生改变。

    服了 TypeScript 这货了,符合我对微软的刻板印象,最近几年似乎每次碰到看不明白的语法出错,都是在 TypeScript ,这货无数细节太微妙了。

    interface Elements { div: { id: number; name?: string; color: string; x: string } a: { id: number; name?: string; href: string; x: string } } declare function getProps<T extends Record<any, any>>(obj: T, keys: (keyof T)[]): void function Foo<T extends 'div' | 'a'>(tag: T, props: Omit<Elements[T], 'x'>) { // 这行报错 Type 'string' is not assignable to type 'Exclude<keyof Elements[T], "x">' getProps(props, ['id']) } Foo('a', { id: 1, href: '#' }) // 我以为是因为 Omit 只能放单个对象,试了下多个也行啊。 type kkk = Omit<Elements['div' | 'a'], 'x'> 把 `function Foo<T extends 'div' | 'a'>(tag: T, props: Omit<Elements[T], 'x'>)` 改成 `function Foo<T extends 'div' | 'a'>(tag: T, props: Omit<Elements['div' | 'a'], 'x'>)` 就不会报错。 那这个 `T` 不就是 `'div' | 'a'` 吗?这 tm 二者到底有什么微妙区别导致代码一个报错一个没错? 
    10 条回复    2024-09-02 17:52:39 +08:00
    ZZITE
        1
    ZZITE  
       2024-09-02 10:59:47 +08:00
    我觉得应该在保持第一种写法的情况下,在 getProps 传参这里使用断言处理,即 getProps(props, ["id"] as (keyof Omit<Elements[T], 'x'>)[]);

    和 Omit 无关,两种情况的区别在于第一种 Omit<Elements[T], 'x'>的结果是泛型参数,这里 TS 是不会做出“ ‘id’一定存在于这个泛型中”的这种推断的,它无法确定具体的类型结构。这里的报错具体来说其实是“ “id' is not assignable to type 'Exclude<keyof Elements[T], "x">'”。但我们知道"id"是有效的 key,所以在传入“id”的时候对它做个类型断言。

    至于第二种写法,Omit<Elements['div' | 'a']是一个具体的联合类型,ts 是可以确定整个类型结构的,也可以推断出"id"是一个符合联合类型结果的有效 key
    june4
        2
    june4  
    OP
       2024-09-02 11:06:25 +08:00
    @ZZITE 可是这个 T 已经在函数类型参数声名中限制只能是 'div' | 'a' 了啊?'id'是一定存在于这个 props 里的。
    june4
        3
    june4  
    OP
       2024-09-02 11:09:24 +08:00
    @june4 这里的问题是 Omit<Elements[T], 'x'> 这里的 Omit 操作结果出问题了。如果去掉 Omit ,直接用 Elements[T],那一点问题都没有。 但这个 Omit 我似乎也看不出为什么会出问题?
    duli950523
        4
    duli950523  
       2024-09-02 11:27:06 +08:00
    @june4 #2 ts 能力是由限的,你觉得是,但是它处理起来就比较麻烦,这也是 as 的作用之一,用在你比编译器知道更多的信息,一般给泛型套上另一个 Conditional Type 大部分就不会继续计算了,所以没必要纠结这些
    csl123
        5
    csl123  
       2024-09-02 11:47:45 +08:00
    Omit 改成 Exclude 好像就可以了,原因没细看。

    ``` typescript

    interface Elements {
    div: {
    id: number; name?: string; color: string; x: string
    }
    a: { id: number; name?: string; href: string; x: string }
    }

    type E<T extends 'div' | 'a'> = Exclude<keyof Elements[T], 'x'>

    // type EDiv = "id" | "name" | "color"
    type EDiv = E<'div'>
    // type EA = "id" | "name" | "href"
    type EA = E<'a'>



    type O<T extends 'div' | 'a'> = Omit<keyof Elements[T], 'x'>

    /**
    * type ODiv = {
    toString: () => string;
    valueOf: () => string;
    toLocaleString: () => string;
    }
    */
    type ODiv = O<'div'>
    /**
    * type OA = {
    toString: () => string;
    valueOf: () => string;
    toLocaleString: () => string;
    }
    */
    type OA = O&t;'a'>
    ```
    ZZITE
        6
    ZZITE  
       2024-09-02 11:53:36 +08:00
    @june4 #3 嗯嗯,这里 Elements[T]如你所说,通过 T extends 'div' | 'a' 是可以推断出具体类型结构的,但在 Omit 后返回了一个新类型,这个新类型是抽象的,就是我说的泛型参数,这里是无法推断出 id 一定存在于新类型的类型结构里的。
    june4
        7
    june4  
    OP
       2024-09-02 12:18:01 +08:00
    @ZZITE 也许是这样。
    我正在读一个库的代码,这个是我自己抽出来改造的一部分实验代码,我这方法行不通。我的类型和库的不同,库的明显更繁琐,我突然有点理解为啥库要舍近求远那么设计类型了, 也许就是因为不那么干是条死路
    哎看大能的代码有一点不爽就是看不到别人的思路过程只看到最终结果。
    zhangdroid
        8
    zhangdroid  
       2024-09-02 12:38:12 +08:00
    可以看看这个 issue: https://github.com/microsoft/TypeScript/issues/39556

    这样就可以 work 了:

    ```ts
    interface Elements {
    div: { id: number; name?: string; color: string; x: string }
    a: { id: number; name?: string; href: string; x: string }
    }

    declare function getProps<T extends Record<any, any>>(obj: T, keys: (keyof T)[]): void

    type SimpleUnionOmit<T, K extends string | number | symbol> = T extends unknown ? Omit<T, K> : never

    function Foo<T extends 'div' | 'a'>(tag: T, props: SimpleUnionOmit<Elements[T], 'x'>) {
    getProps(props, ['id'])
    }
    ```
    Xu3Xan89YsA7oP64
        9
    Xu3Xan89YsA7oP64  
       2024-09-02 12:44:07 +08:00
    any 、 @ts-ignore 梭到底
    cosmos601
        10
    cosmos601  
       2024-09-02 17:52:39 +08:00
    写这种复杂函数的 ts 类型是真的想死,半小时写完的业务代码,搞类型半小时
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     5464 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 25ms UTC 08:10 PVG 16:10 LAX 01:10 JFK 04:10
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86