有没有 Typescript 的大佬帮忙看一个泛型接口的问题 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
HanMeiM

有没有 Typescript 的大佬帮忙看一个泛型接口的问题

  •  
  •   HanMeiM 2020 年 3 月 3 日 3373 次点击
    这是一个创建于 2245 天前的主题,其中的信息可能已经有所发展或是发生改变。

    先上代码

    export interface Store { [key: string]: any; } type OnFinishCallback= (values: Store) => void; export interface Callbacks { onFinish?: OnFinishCallback; } export interface TestData { name: string; password: string; age: number; } const test1: OnFinishCallback= (values: TestData) => {}; const test2: Callbacks['onFinish'] = (values: TestData) => {}; 

    抛出问题

    image.png

    Typescript 版本:3.7.5

    提问:为什么不能匹配泛型接口?单纯好奇,也知道可以通过 <T> 或者中间函数传递来解决。

    25 条回复    2020-07-14 13:53:11 +08:00
    johnnyNg
        1
    johnnyNg  
       2020 年 3 月 3 日
    说的是 TestData 和 Store 类型不符合,把 TestData 改成 Store 就好了
    mxT52CRuqR6o5
        2
    mxT52CRuqR6o5  
       2020 年 3 月 3 日 via Android
    泛型在哪?
    HanMeiM
        3
    HanMeiM  
    OP
       2020 年 3 月 3 日
    @mxT52CRuqR6o5 害,俺说错了,泛型能匹配上,但是直接这样写 索引签名 不行。
    chuxiaonan
        4
    chuxiaonan  
       2020 年 3 月 3 日
    export interface TestData extends Store {
    ...
    }
    HanMeiM
        5
    HanMeiM  
    OP
       2020 年 3 月 3 日
    @chuxiaonan 不行哦,我试过的。
    chuxiaonan
        6
    chuxiaonan  
       2020 年 3 月 3 日
    @HanMeiM 这样噢 那就不是很了解啦
    myl0204
        7
    myl0204  
       2020 年 3 月 3 日
    我的理解是实际函数的参数类型是确定的:`TestData `,而 OnFinishCallback 中参数类型其实是不定的,可以是`{a: 1, b:2}`也可以是`{c: 3}`等等。

    这二者当然不匹配呀
    maichael
        8
    maichael  
       2020 年 3 月 3 日
    因为 Store 类型的值不能赋值给 TestData 类型的变量。你对泛型的理解是有问题的,你想,假设你整个逻辑成立,那你这里的 test1 函数入参就是 Store 类型的变量,但是 Store 类型的变量未必能赋值给 TestData 类型的变量,所以逻辑上不通。反过来才是对的,但意图就可能跟你原有的不一致了,你可以说下你的需求是啥吗

    type OnFinishCallback= (values: TestData) => void;

    const test1: OnFinishCallback= (values: Store) => {};
    geelaw
        9
    geelaw  
       2020 年 3 月 3 日   3
    Store 是水果,TestData 是橘子,OnFinishCallback 是“能处理任意水果”的类型。因为 test1 初始化的表达式不能处理任意水果(只能处理橘子),所以不行。

    用术语来说是 A => void 对 A 是逆变,如果 is-an A,则 (A => void) is-a (B => void),而不是反过来。
    HanMeiM
        10
    HanMeiM  
    OP
       2020 年 3 月 3 日
    @maichael
    @geelaw
    其实也不是我写成这样,我肯定还是希望用泛型去解决。
    我还是直接说我在哪遇到的问题吧。
    我今天更新 Ant-Design 4.0.0 的时候,Form 组件有很大的改变。
    官方推荐使用 `Form` 组件的 `onFinish` 属性来获取表单值。
    下面是这个属性的定义。

    ![image.png]( https://i.loli.net/2020/03/03/fGa5TFlbCqAWZtJ.png)

    ![image.png]( https://i.loli.net/2020/03/03/JOx3lFEbDmCwWqA.png)

    ![image.png]( https://i.loli.net/2020/03/03/Um3qOG2BRAMy4xt.png)

    然后上图的 `test2` 就是我写的获取表单的方法,很明显会报错,所以有点头疼。
    目前是直接在 `Form` 表单中这样写来解决

    ```Typescript
    OnFinish={(value: Store) => test2(value)}
    ```
    maichael
        11
    maichael  
       2020 年 3 月 3 日   1
    @HanMeiM #10 暂时没有,这个 Antd 这个东西的问题。

    https://github.com/ant-design/ant-design/issues/21195

    https://github.com/react-component/field-form/issues/70

    简单来说,他这里本应该提供 "Generic type arguments" 来自定义 StoreValue 的类型,但他没有给,所以你从外部没法去改变他内部的类型。你现在只能强转类型然后等他修好。
    HanMeiM
        12
    HanMeiM  
    OP
       2020 年 3 月 3 日
    @maichael 恩呢,感谢
    buffzty
        13
    buffzty  
       2020 年 3 月 3 日
    TestData 类型是兼容 Store 的,但是 (v: TestData )=>{} 这个函数类型 是不兼容 (v: Store)=>{} 这个函数类型的
    可能是 ts 为了检测简单,不然还要把所有函数的参数再检测一遍.

    我的建议:

    去掉函数形参中的类型,此时 ts 会自动推断为 Store
    传参时可以传 TestData 类型.
    以下代码在 3.7.5 无报错
    ```typescript
    export interface Store {
    title: string // 加这个只是为了测试自动推断
    [key: string]: any
    }
    type OnFinishCallback= (values: Store) => void
    export interface Callbacks {
    onFinish?: OnFinishCallback
    }
    export type TestData = Store & {
    name: string
    password: string
    age: number
    }
    let a: TestData = { b: 123, age: 12, name: '1', password: '', title: '' }
    let b: Store = a
    const test1: OnFinishCallback= values => {
    console.log(values.title) // 这里的 values 已经被自动推断过了
    }

    const test2: Callbacks['onFinish'] = values => {
    console.log(values.title)
    }
    test1(a)
    test1(b)
    test2(a)
    test2(b)

    ```
    Mistwave
        14
    Mistwave  
       2020 年 3 月 3 日
    对于函数类型,入参是逆变的,而返回值是协变的。
    也就是说,参数类型可以用其超类型替代,返回类型可以用子类型替代,而不能反过来。
    例子可以参考 @geelaw 的回复
    HanMeiM
        15
    HanMeiM  
    OP
       2020 年 3 月 3 日
    @Mistwave
    @geelaw
    学习了学习了
    HanMeiM
        16
    HanMeiM  
    OP
       2020 年 3 月 3 日
    @buffzty 你这个方法已经放弃了 Typescript 的优势了
    voidcomma
        17
    voidcomma  
       2020 年 3 月 3 日
    TestData 的类型显然更具体了,它的类型被收窄(narrow down)了,test1 中的 values 的限制更多,不匹配 OnFinishCallback 中的 Store。也就是,OnFinishCallback 的 values 参数能够接受任意的对象,而你给 test1 的 values 定义的参数中只能包含 name, password, age 这三个属性。
    不需要给 test1 中的 values 定义类型 TestData,它会被推断为 Store。
    buffzty
        18
    buffzty  
       2020 年 3 月 3 日
    @HanMeiM 为什么去掉形参类型名叫放弃优势呢? 去掉之后什么代码也没多写,类型也自动推断了. 你不就是要这个效果吗 本来能自动推断的,你非要给他加个错误类型
    HanMeiM
        19
    HanMeiM  
    OP
       2020 年 3 月 3 日
    @buffzty 很明显这个 Store 应该是个泛型,只不过他这里写的有问题。你实际写代码的时候,看你代码的人能通过 Store 知道什么呢?我能知道这个表单有哪些数据?这个数据是什么类型?我还是得看上下文代码。
    maichael
        20
    maichael  
       2020 年 3 月 4 日
    @HanMeiM #12

    const test1: OnFinishCallback= ((values: TestData) => {}) as OnFinishCallback

    一个勉强用着的解决方法。
    jchencode
        21
    jchencode  
       2020 年 5 月 17 日
    老哥,想问下你们 [email protected] 怎么用 ts 写表单,有代码可以参考下吗,感谢!
    HanMeiM
        22
    HanMeiM  
    OP
       2020 年 5 月 17 日
    @LiuJiang 我个人是这么写的

    const handleFinish = (fieldsValue: IFieldsValue) => {
    ...
    }

    <Form OnFinish={(value: any) => handleFinish(value)}>
    ...
    </Form>
    jchencode
        23
    jchencode  
       2020 年 5 月 18 日
    @HanMeiM 请问 IFieldsValue 这个是啥类型?
    HanMeiM
        24
    HanMeiM  
    OP
       2020 年 5 月 18 日
    @LiuJiang ......你自己定义的表单应该返回的类型
    uxstone
        25
    uxstone  
       2020 年 7 月 14 日
    Antd V4 文档中的示例代码 TypeScript 和 Javascript 是一样的
    蛋疼,感觉与 TypeScript 整合这块还没有 V3 的好用
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     1704 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 58ms UTC 16:16 PVG 00:16 LAX 09:16 JFK 12:16
    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