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

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

  •  1
     
  •   june4 · 61 天前 · 1601 次点击
    这是一个创建于 61 天前的主题,其中的信息可能已经有所发展或是发生改变。

    服了 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  
       61 天前
    我觉得应该在保持第一种写法的情况下,在 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
       61 天前
    @ZZITE 可是这个 T 已经在函数类型参数声名中限制只能是 'div' | 'a' 了啊?'id'是一定存在于这个 props 里的。
    june4
        3
    june4  
    OP
       61 天前
    @june4 这里的问题是 Omit<Elements[T], 'x'> 这里的 Omit 操作结果出问题了。如果去掉 Omit ,直接用 Elements[T],那一点问题都没有。 但这个 Omit 我似乎也看不出为什么会出问题?
    duli950523
        4
    duli950523  
       61 天前
    @june4 #2 ts 能力是由限的,你觉得是,但是它处理起来就比较麻烦,这也是 as 的作用之一,用在你比编译器知道更多的信息,一般给泛型套上另一个 Conditional Type 大部分就不会继续计算了,所以没必要纠结这些
    csl123
        5
    csl123  
       61 天前
    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<'a'>
    ```
    ZZITE
        6
    ZZITE  
       61 天前
    @june4 #3 嗯嗯,这里 Elements[T]如你所说,通过 T extends 'div' | 'a' 是可以推断出具体类型结构的,但在 Omit 后返回了一个新类型,这个新类型是抽象的,就是我说的泛型参数,这里是无法推断出 id 一定存在于新类型的类型结构里的。
    june4
        7
    june4  
    OP
       61 天前
    @ZZITE 也许是这样。
    我正在读一个库的代码,这个是我自己抽出来改造的一部分实验代码,我这方法行不通。我的类型和库的不同,库的明显更繁琐,我突然有点理解为啥库要舍近求远那么设计类型了, 也许就是因为不那么干是条死路🐶
    哎看大能的代码有一点不爽就是看不到别人的思路过程只看到最终结果。
    zhangdroid
        8
    zhangdroid  
       61 天前
    可以看看这个 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'])
    }
    ```
    shizhibuyu2023
        9
    shizhibuyu2023  
       61 天前
    any 、 @ts-ignore 梭到底
    cosmos601
        10
    cosmos601  
       61 天前
    写这种复杂函数的 ts 类型是真的想死,半小时写完的业务代码,搞类型半小时
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1766 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 19ms · UTC 16:32 · PVG 00:32 · LAX 09:32 · JFK 12:32
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.