You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
对于js的对象,我们可以表示为object[key]。ts也有类似的,即索引访问T[K]。如果T是object的interface或者type、K是key的话,则表示object[key]具有类型T[K]。而这个K不是随便来的,一般需要索引类型查询操作符keyof的作用下返回了索引查询(number 、string类型的key)才会有效,否则报类似Type 'K' cannot be used to index type 'T'的错误。
// 这种时候,可能就开始写any了。因为不知道传入的是什么functiongetValue(o: any,k: string): any{returno[k]}getValue({a: 1,b: '2'},'a');// 稍微好一点的可能是“觉得这是对象所以是object”// function get(o: object, k: string): any ,但返回值还是any// 如果不用any,那就报错object没有属性xxx,😢
此时,keyof和泛型配合起来就可以告别any了:
// K extends keyof V 保证第二个泛型参数是属于o的一个keyfunctiongetValue<V,KextendskeyofV>(o: V,k: K): V[K]{returno[k]}getValue<{a: number;b: string;},'a'>({a: 1,b: '2'},'a');
按照常规的思维,key也就是那三种类型了,写死,然后泛型K就是key值 function getValue<V, K>(o: V, k: string | number | symbol): V[K]
但是这样就会报错:Type 'K' cannot be used to index type 'V',就是因为没有什么约束条件(如keyof操作符保证返回合法的key),K是什么也不知道,所以就直接报错类型K不能用于索引类型V的索引访问
强烈建议使用vscode,因为都是同一家,对ts的支持和开发体验是非常棒的,大大增加了开发效率和质量,避免各种错误。
泛型
定义一种type或者interface,可以传入泛型参数,达到类型复用的效果:
如果再包一层数组
另外,函数也可以传入泛型参数:
在tsx文件中,箭头函数泛型写法有点不一样,因为要避免尖括号被误判:
索引类型
对于js的对象,我们可以表示为object[key]。ts也有类似的,即索引访问T[K]。如果T是object的interface或者type、K是key的话,则表示object[key]具有类型T[K]。而这个K不是随便来的,一般需要索引类型查询操作符keyof的作用下返回了索引查询(number 、string类型的key)才会有效,否则报类似
Type 'K' cannot be used to index type 'T'
的错误。想想就知道,没有任何其他条件或者约束(泛型约束),直接这样用T[K],ts怎么可能知道这是什么类型?怎么知道你想干什么?那就报错咯。
keyof
keyof返回某个interface或者type的所有key:
写一个get函数,输入对象和key,返回对应的value
此时,keyof和泛型配合起来就可以告别any了:
换一种方式实现,需要考虑undefined
最后,使用方法
这里注意,最后还是要写死'a',为什么呢?因为ts只能帮到你在写代码的时候,明确的告诉ts我要取a的值。如果依赖用户输入的那种key,已经脱离了ts力所能及的范围。此时在vscode中,逻辑还是可以写着先,只是没有享受到返回值类型推断的这种福利
类似三元的语法
上面有一段
K extends keyof V ? V[K] : undefined
,这是ts的condition type
,但是前面的condition只能使用extends的语句。比如像antd一些组件,仅仅有几种值:如果我们想写一个类型逻辑:是那几种type的就是那几种,否则返回default的type,那么就可以使用
condition type
了如果想写一个getType函数,保证输入的type一定是那几个的一种:
window as any
有时候,我们想给window加上一些辅助变量,发现会报错:
此时可能就会给window 强行as any了:
这样做,报错是解决了,但是又是依赖了any,而且还不能享受到在vsc写代码的时候,对window.a的代码提示。如果再次需要读取或者赋值window.a,那又要(window as any).a了。其实,优雅的解决方法是interface。interface可以写多个重名,多个会合并
动态修改的情况
我们使用其他方法修改了一些属性,比如装饰器、对象assign,ts代码肯定是标红的,但实际上我们都知道那是没有问题的:
由于后面也是人为的加上的属性b,那么我们只能一开始的时候就直接声明b属性:
使用装饰器的时候,我们给Greeter类加上console方法。但是使用的时候会说
Property 'console' does not exist on type 'Greeter'
。当然,使用的时候(this as any).console(this.wording)
就可以解决了实际上,和wording也是同理,事先声明一下console即可优雅解决:
mobx和react一起使用的时候,也是有类似场景:
react router的路由匹配的params也是会有这个情况:
不懂其他库的类型系统就点进去看源码
比如上面的RouteComponentProps,按住cmd点击进入,发现其源码如下
这就很明显的,告诉我们匹配到的参数就在props.match.params里面。这不仅知道了结构,还相当于半个文档,看一下命名就知道是做什么的了
本身没有d.ts文件的库怎么办
如果他不支持,ts也会报错: 无法找到模块“xxx”的声明文件。import就报错了,as any大法都不行了!
既然他没有,那就帮他写。来到自己项目的d.ts根文件下,加入声明:
最后,给出如何编写d.ts的常见几种模块化方案:
一些高级的泛型类型
使用ts基本语法和关键字,可以实现一些高级的特性(如Partial,Required,Pick,Exclude,Omit等等),增加了类型复用性。按住cmd,再点击这些类型进入ts源码里面(lib.es5.d.ts)的看到一些内置类型的实现:
总结
The text was updated successfully, but these errors were encountered: