精华内容
下载资源
问答
  • typescript cannot infer type

    2020-12-07 01:28:32
    <p>Typescript cannot infer correctly the type, am I wrong somewhere? <p><img alt="Screenshot from 2019-03-30 13-00-47" src=...
  • 理解TypeScript中的infer关键字
    展开全文
  • Dooring特别版)在线IDE开发入门之从零实现一个在线代码编辑器基于React+Koa实现一个h5页面可视化编辑器-DooringTS核心知识点总结及项目实战案例分析前言以前一直不会用infer,要么直接就是returnType,压根不需要用...

    ebc394229925b5193288328d52663101.png

    往期精彩

    • 基于NodeJS从零构建线上自动化打包工作流(H5-Dooring特别版)
    • 在线IDE开发入门之从零实现一个在线代码编辑器
    • 基于React+Koa实现一个h5页面可视化编辑器-Dooring
    • TS核心知识点总结及项目实战案例分析

    前言

    以前一直不会用infer,要么直接就是returnType,压根不需要用infer,网上那些教程只给示例不给具体场景就无法让人很好理解这玩意。

    类型分发

    对于infer,最好应该先说一下类型分发,虽然他们关系不是太大,但是如果把infer与类型分发结合起来,让人一看就觉得这人ts水平可以。至于协变逆变等概念会比较容易让人搞混乱,可以以后再掌握。

    我以前也学过这个,但是并不是能完全掌握它的使用时机,也不知道如何用,所以看别人用能看懂和自己能用完全是2种状态。

    首先看一下类型分发的基本例子:
    interface Fish {    fish: string}interface Water {    water: string}interface Bird {    bird: string}interface Sky {    sky: string}//naked typetype Condition = T extends Fish ? Water : Sky;let condition1: Condition = { water: '水' };let condition2: Condition = { sky: '天空' };

    相信这个例子大家很容易理解,但是实际中什么时候用,怎么用,完全不知道。

    这个例子有个特点,就是下面的condition1condition2里定义的类型里所传的泛型与后面赋值的类型并不一样。

    也就是说,类型分发一般是用来先知道已知类型,赋的值的类型会基于这个分发进行判断推出相应类型。

    乍看之下好像还是没什么卵用,比如condition1,我都知道类型,我直接写个Sky|Water类型不香?干嘛二货的还搞个类型分发?

    上面那个例子确实没啥卵用,但是如果判断继承的也是泛型,那么就可以快速取出一些类型,而不用自己重新去定义:(虽然这些很多都是内置的)

    type Diff = T extends U ? never : T;type R = Diff<"a" | "b" | "c" | "d", "a" | "c" | "f">;  // "b" | "d"type Filter = T extends U ? T : never;type R1 = Filter<string | number | booleannumber>;

    既然有内置的,还不是没卵用。。。所以这就需要和infer联合使用才能看出牛b之处。

    infer初探

    infer大家应该都知道,returnType就是infer搞得,代码是这样:
    type ReturnTypeextends (...args: any[]) => any> = T extends (...args: any[]) => infer R ? R : any;

    乍看之下好像有点难懂,其实仔细看发现还是很好理解的,它也是个类型分发。学到这里,很多人可能就只知道有这个东西,但是什么时候用Infer完全不知道,我也是这样,后来再次听课时突发灵感,发现这个infer其实就相当于占位,也就是一个不知道的类型,用infer X去给他占位,再结合类型分发,就能玩出花样来了。当时还有小伙伴这么问:ts不是能自动推断类型吗?为什么需要Infer X去推断类型。卧槽,这个问的太好了,这个就是理解Infer的关键。我们先结合个示例来进行说明
    export {}type Parameters = T extends (...args: infer R) => any ? R : any;type T0 = Parameters<() => string>;  // []type T1 = Parameters<(s: string) => void>;  // [string]type T2 = Parameters<((arg: T) => T)>;  // [unknown]

    这个parameter也是内置的,可以看见,也是个类型分发,跟returnType区别就是infer X的占位跑到参数上去定义类型了。

    如果我们把infer R换成已知类型,那么这个类型分发就跟一开始的demo没太大区别:

    type Parameters = T extends (...args:string[]) => any ? string[] : any;type T0 = Parameters<() => string>; 

    如果不换成已知类型,那么只写R不写infer会报错,因为不知道R是什么东西。

    那么如果通过泛型传呢?可惜args必须是个数组类型,所以用泛型传还得限定下它的条件:

    type Parametersextends type T0 = Parameters<() => string,string[]>; 

    可以发现,这么传跟已知类型传其实没太大区别,因为在传第二个泛型的时候,这个类型我们是知道的,所以这种情况,也没什么太大用处,除非传泛型的是另一个人,那么我们在写这个库的时候,倒是可以拿到用户所定义的类型。这时倒是有点作用。

    这样一换就可以发现,infer可以在类型推导中去占任何位置,最后的推导的类型可以借助这之间所需的类型。可以看下这个例子加深理解:

      type T1 = { name: string };  type T2 = { age: number };    type UnionToIntersection = T extends { a: (x: infer U) => void; b: (x: infer U) => void } ? U : never;  type T3 = UnionToIntersection(x: T1) => 

    这个例子就是infer取得参数,两个函数的参数,对于为啥2个会出来交叉类型,这里是协变,所以是交叉类型。

    下面看一下更难点的例子,来源于leetcode招聘:

    https://github.com/LeetCode-OpenSource/hire/blob/master/typescript_zh.md

    题目是这样:

    interface Action {    payload?: T;    type: string;  }    class EffectModule {    count = 1;    message = "hello!";      delay(input: Promise<number>) {      return input.then(i => ({        payload: `hello ${i}!`,        type: 'delay'      }));    }      setMessage(action: Action<Date>) {      return {        payload: action.payload!.getMilliseconds(),        type: "set-message"      };    }  }    // 修改 Connect 的类型,让 connected 的类型变成预期的类型  type Connect = (module: EffectModule) => any;    const connect: Connect = m => ({    delay: (input: number) => ({      type: 'delay',      payload: `hello 2`    }),    setMessage: (input: Date) => ({      type: "set-message",      payload: input.getMilliseconds()    })  });    type Connected = {    delay(input: number): Action<string>;    setMessage(action: Date): Action<number>;  };    export const connected: Connected = connect(new EffectModule());

    要求修改那个any,使其返回正确类型,而且这个类型要和connected一样。

    有同学说,直接把any改成connected不就完了?要是这么简单也不会出这题。这个肯定是要你推出来,并且这个connected它的类型是EffectModule实例上的方法,里面的参数与返回还修改了。

    这题怎么做呢,先一步步来,先提取出effectModule的方法,不然没法下一步。

    提取class方法没有现成的,肯定不能keyof EffectModule,因为还有别的东西,怎么排除别的玩意呢?就是利用类型分发和class可以取值来做,如果是函数,那就提取,否则就不提取:

      type MethodName = {[F in keyof T]:T[F] extends Function ? F:never}[keyof T]  type EE =  MethodName

    这里同时利用value如果是never 则keyof就不会返回。这段其实挺有启发性,因为很多时候,都想搞个循环判断类型,然后进行选择,这就是个很好的范例。

    拿到了name然后要改装方法它需要:

    asyncMethod(input: Promise): Promise> asyncMethod(input: T): Action syncMethod(action: Action): Action  syncMethod(action: T): Action
    这个是题目给的,直接抄来:
    type asyncMethod = (input: Promise) => Promise> type asyncMethodConnect = (input: T) => Action type syncMethod = (action: Action) => Action type syncMethodConnect = (action: T) => Action 
    然后需要做一个类型分发,用来判断是哪个方法,再分发给哪个方法:
    type EffectMethodAssign = T extends asyncMethod     ? asyncMethodConnect     : T extends syncMethod     ? syncMethodConnect     : never
    这段很简单,就是分发判断,泛型是用infer占位ok。最后,修改connect,就大功告成.
      type Connect = (module: EffectModule) => {      [F in MethodName<typeof module>]:EffectMethodAssign<typeof module[F]>  }
    展开全文
  • TypeScript `infer` 关键字

    千次阅读 2019-05-28 23:39:00
    考察如下类型: type PromiseType ...如果你对 TypeScript 不是那么陌生,可能知道官方类型库中提供了 ...infer ...infer ...infer ...转载于:https://www.cnblogs.com/Wayou/p/typescript_infer.html

    考察如下类型:

    type PromiseType<T> = (args: any[]) => Promise<T>;

    那么对于符合上面类型的一个方法,如何得知其 Promise 返回的类型?

    譬如对于这么一个返回 string 类型的 Promise:

    async function stringPromise() {
      return "string promise";
    }

    RetrunType

    如果你对 TypeScript 不是那么陌生,可能知道官方类型库中提供了 RetrunType 可获取方法的返回类型,其用法如下:

    type stringPromiseReturnType = ReturnType<typeof stringPromise>; // Promise<string>

    确实拿到了方法的返回类型,不过是 Promise<string>。但其实是想要返回里面的 string,所以和我们想要的还差点意思。

    既然都能从一个方法反解其返回类型,肯定还能从 Promsie<T> 中反解出 T。所以不不妨看看 ReturnType 的定义:

    /**
     * Obtain the return type of a function type
     */
    type ReturnType<T extends (...args: any) => any> = T extends (...args: any) => infer R ? R : any;

    F12 一看,果然发现了点什么,这里使用了 infer 关键字。

    条件类型及 infer

    上面 T extends U ? X : Y 的形式为条件类型(Conditional Types),即,如果类型 T 能够赋值给类型 U,那么该表达式返回类型 X,否则返回类型 Y

    所以,考察 ReturnType的定义,

    type ReturnType<T extends (...args: any) => any> = T extends (...args: any) => infer R ? R : any;

    如果传入的类型 T 能够赋值给 (...args: any) => R 则返回类型 R

    但是这里类型 R 从何而来?讲道理,泛型中的变量需要外部指定,即 RetrunType<T,R>,但我们不是要得到 R 么,所以不能声明在这其中。这里 infer 便解决了这个问题。表达式右边的类型中,加上 infer 前缀我们便得到了反解出的类型变量 R,配合 extends 条件类型,可得到这个反解出的类型 R。这里 R 即为函数 (...args: any) => R 的返回类型。

    反解 Promise

    有了上面的基础,推而广之就很好反解 Promise<T> 中的 T 了。

    type PromiseType<T> = (args: any[]) => Promise<T>;
    
    type UnPromisify<T> = T extends PromiseType<infer U> ? U : never;

    测试 UnPromisify<T>

    async function stringPromise() {
      return "string promise";
    }
    
    async function numberPromise() {
      return 1;
    }
    
    interface Person {
      name: string;
      age: number;
    }
    
    async function personPromise() {
      return { name: "Wayou", age: 999 } as Person;
    }
    
    type extractStringPromise = UnPromisify<typeof stringPromise>; // string
    
    type extractNumberPromise = UnPromisify<typeof numberPromise>; // number
    
    type extractPersonPromise = UnPromisify<typeof personPromise>; // Person

    解析参数数组的类型

    反解还可用在其他很多场景,比如解析函数入参的类型。

    type VariadicFn<A extends any[]> = (...args: A) => any;
    type ArgsType<T> = T extends VariadicFn<infer A> ? A : never;
     
    type Fn = (a: number, b: string) => string;
    type Fn2Args = ArgsType<Fn>; // [number, string]

    相关资源

    转载于:https://www.cnblogs.com/Wayou/p/typescript_infer.html

    展开全文
  • <div><p>I met a problems here. And here is my solution. I wonder if there is any other solution which is better? <pre><code> props:{ src: { type: String, required: true } }, setup(props){ ...
  • typescriptinfer的理解与使用

    千次阅读 2020-08-28 07:45:43
    前言 以前一直不会用infer,要么直接就是returnType,压根不需要用infer,网上那些教程只给示例不给具体场景就无法让人很好理解这玩意,并且很好

    前言

    • 以前一直不会用infer,要么直接就是returnType,压根不需要用infer,网上那些教程只给示例不给具体场景就无法让人很好理解这玩意。

    类型分发

    • 对于infer,最好应该先说一下类型分发,虽然这2关系不是太大,但是如果把infer与类型分发结合起来,让人一看就觉得这人ts水平可以。至于协变与逆变等概念会比较容易让人搞混乱,可以以后再掌握。
    • 我以前也学过这个,但是并不是能完全掌握它的使用时机,也不知道如何用,所以看别人用能看懂和自己能用完全是2种状态。
    • 首先看一下类型分发的基本例子:
    interface Fish {
        fish: string
    }
    interface Water {
        water: string
    }
    interface Bird {
        bird: string
    }
    interface Sky {
        sky: string
    }
    //naked type
    type Condition<T> = T extends Fish ? Water : Sky;
    
    let condition1: Condition<Fish | Bird> = { water: '水' };
    let condition2: Condition<Fish | Bird> = { sky: '天空' };
    
    • 相信这个例子大家很容易理解,但是实际中什么时候用,怎么用,完全不知道。
    • 这个例子有个特点,就是下面的condition1和condition2里定义的类型里所传的泛型与后面赋值的类型并不一样。
    • 也就是说,类型分发一般是用来先知道已知类型,赋的值的类型会基于这个分发进行判断推出相应类型。
    • 乍看之下好像还是没什么卵用,比如condition1,我都知道类型,我直接写个Sky|Water类型不香?干嘛二货的还搞个类型分发?
    • 上面那个例子确实没啥卵用,但是如果判断继承的也是泛型,那么就可以快速取出一些类型,而不用自己重新去定义:(虽然这些很多都是内置的)
    type Diff<T, U> = T extends U ? never : T;
    
    type R = Diff<"a" | "b" | "c" | "d", "a" | "c" | "f">;  // "b" | "d"
    
    type Filter<T, U> = T extends U ? T : never;
    type R1 = Filter<string | number | boolean, number>;
    
    • 既然有内置的,还不是没卵用。。。所以这就需要和infer联合使用才能看出牛b之处。

    infer

    • infer大家应该都知道,returnType就是infer搞得,代码是这样:
    type ReturnType<T extends (...args: any[]) => any> = T extends (...args: any[]) => infer R ? R : any;
    
    • 乍看之下好像有点难懂,其实仔细看发现还是很好理解的,它也是个类型分发。
    • 学到这里,很多人可能就只知道有这个东西,但是什么时候用Infer完全不知道,我也是这样,后来再次听课时突发灵感,发现这个infer其实就相当于占位,也就是一个不知道的类型,用infer X去给他占位,再结合类型分发,就能玩出花样来了。当时还有小伙伴这么问:ts不是能自动推断类型吗?为什么需要Infer X去推断类型。卧槽,这个问的太好了,这个就是理解Infer的关键。
    • 我们先结合个示例来进行说明:
    export {}
    type Parameters<T> = T extends (...args: infer R) => any ? R : any;
    type T0 = Parameters<() => string>;  // []
    type T1 = Parameters<(s: string) => void>;  // [string]
    type T2 = Parameters<(<T>(arg: T) => T)>;  // [unknown]
    
    • 这个parameter也是内置的,可以看见,也是个类型分发,跟returnType区别就是infer X的占位跑到参数上去定义类型了。
    • 如果我们把infer R换成已知类型,那么这个类型分发就跟一开始的demo没太大区别:
    type Parameters<T> = T extends (...args:string[]) => any ? string[] : any;
    type T0 = Parameters<() => string>; 
    
    • 如果不换成已知类型,那么只写R不写infer会报错,因为不知道R是什么东西。
    • 那么如果通过泛型传呢?可惜args必须是个数组类型,所以用泛型传还得限定下它的条件:
    type Parameters<T,R extends Array<any>> = T extends (...args:R) => any ? R : any;
    
    type T0 = Parameters<() => string,string[]>; 
    
    • 可以发现,这么传跟已知类型传其实没太大区别,因为在传第二个泛型的时候,这个类型我们是知道的,所以这种情况,也没什么太大用处,除非传泛型的是另一个人,那么我们在写这个库的时候,倒是可以拿到用户所定义的类型。这时倒是有点作用。
    • 这样一换就可以发现,infer可以在类型推导中去占任何位置,最后的推导的类型可以借助这之间所需的类型。可以看下这个例子加深理解:
      type T1 = { name: string };
      type T2 = { age: number };
      
      type UnionToIntersection<T> = T extends { a: (x: infer U) => void; b: (x: infer U) => void } ? U : never;
      type T3 = UnionToIntersection<{ a: (x: T1) => void; b: (x: T2) => void }>; // T1 & T2
    
    interface Action<T> {
        payload?: T;
        type: string;
      }
      
      class EffectModule {
        count = 1;
        message = "hello!";
      
        delay(input: Promise<number>) {
          return input.then(i => ({
            payload: `hello ${i}!`,
            type: 'delay'
          }));
        }
      
        setMessage(action: Action<Date>) {
          return {
            payload: action.payload!.getMilliseconds(),
            type: "set-message"
          };
        }
      }
      
      // 修改 Connect 的类型,让 connected 的类型变成预期的类型
      type Connect = (module: EffectModule) => any;
      
      const connect: Connect = m => ({
        delay: (input: number) => ({
          type: 'delay',
          payload: `hello 2`
        }),
        setMessage: (input: Date) => ({
          type: "set-message",
          payload: input.getMilliseconds()
        })
      });
      
      type Connected = {
        delay(input: number): Action<string>;
        setMessage(action: Date): Action<number>;
      };
      
      export const connected: Connected = connect(new EffectModule());
    
    • 要求修改那个any,使其返回正确类型,而且这个类型要和connected一样。
    • 有同学说,直接把any改成connected不就完了?要是这么简单也不会出这题。这个肯定是要你推出来,并且这个connected它的类型是EffectModule实例上的方法,里面的参数与返回还修改了。
    • 这题怎么做呢,先一步步来,先提取出effectModule的方法,不然没法下一步。
    • 提取class方法没有现成的,肯定不能keyof EffectModule,因为还有别的东西,怎么排除别的玩意呢?就是利用类型分发和class可以取值来做,如果是函数,那就提取,否则就不提取:
      type MethodName<T> = {[F in keyof T]:T[F] extends Function ? F:never}[keyof T]
      type EE =  MethodName<EffectModule>
    
    • 这里同时利用value如果是never 则keyof就不会返回。这段其实挺有启发性,因为很多时候,都想搞个循环判断类型,然后进行选择,这就是个很好的范例。
    • 拿到了name然后要改装方法它需要:
    asyncMethod<T, U>(input: Promise<T>): Promise<Action<U>>  变成了
    asyncMethod<T, U>(input: T): Action<U> 
    syncMethod<T, U>(action: Action<T>): Action<U>  变成了
    syncMethod<T, U>(action: T): Action<U>
    
    • 这个是题目给的,直接抄来:
    type asyncMethod<T, U> = (input: Promise<T>) => Promise<Action<U>> 
    type asyncMethodConnect<T, U> = (input: T) => Action<U> 
    type syncMethod<T, U> = (action: Action<T>) => Action<U> 
    type syncMethodConnect<T, U> = (action: T) => Action<U> 
    
    • 然后需要做一个类型分发,用来判断是哪个方法,再分发给哪个方法:
    type EffectMethodAssign<T> = T extends asyncMethod<infer U, infer V>? 
                                                asyncMethodConnect<U, V>
                                            : T extends syncMethod<infer U, infer V>
                                            ? syncMethodConnect<U, V>
                                            : never
    
    • 这段很简单,就是分发判断,泛型是用infer占位ok。
    • 最后,修改connect,就大功告成
      type Connect = (module: EffectModule) => {
          [F in MethodName<typeof module>]:EffectMethodAssign<typeof module[F]>
      };
    
    展开全文
  • Seems to be safe enough to infer the data type as boolean. <p><strong>System (please complete the following information): OS: Mac OS 10.15.3 IDE: VS Code Plugin/Package: Svelte for VS Code (the new ...
  • infer 最早出现在此 PR 中,表示在 extends 条件语句中待推断的类型变量。 简单示例如下: type ParamType<T> = T extends (param: infer P) => any ? P : T; 复制代码在这个条件语句 T extends (param: ...
  • 在 2.8 版本中,TypeScript 内置了一些与infer 有关的映射类型: 官网 Utility Types 文章目录infer用在函数中构造函数中内置类型ReturnType infer infer 最早出现在此 PR 中,表示在 extends 条件语句中待推断的...
  • 下面看一下更难点的例子,来源于leetcode招聘: https://github.com/LeetCode-OpenSource/hire/blob/master/typescript_zh.md 题目是这样: interface Action { payload?: T; type: string; } class EffectModule {...
  • <div><p>On this import statement: <pre><code> import myComponent from ./myComponent.vue </code></pre> <p>myComponent has the type of Vue....go to defintion"...microsoft/TypeScript-Vue-Starter</p></div>
  • We could try to infer the request params from the URL by using template literal types (introduced in TS 4.1). See this ;4.1.0-pr-40336-88#code/C4TwDgpgBAogHsATgQwMbAEoHsCuwIAKyKAtgM4A8...
  • When working with conditionals types, within the “extends” expression, we can use the “infer” keyword to either get the type of the elements of an array, or even to get the return type of a ...
  • <div><p>Alternative to PR #112 (documented in #109). <strong>Introduces breaking changes</strong></p> <ul><li>Introduce <code>FSAWithPayload</code> and <code>ErrorFSAWithPayload</code></li><li>...

空空如也

空空如也

1 2 3 4 5 ... 13
收藏数 257
精华内容 102
关键字:

infertypescript