首页
统计
墙纸
留言
Search
1
PVE8优化
13 阅读
2
Debian 12 / Ubuntu 22.04 使用源安装 LAMP 教程
11 阅读
3
内核版本 4.9 以上的 Linux 系统开启/关闭 BBR 的方法
10 阅读
4
CSS动画
10 阅读
5
jenkins根据分支、文件夹打包
9 阅读
web前端
Vue
CSS
javascript
React
那些年爬过过的坑
ES6
TypeScrippt
ES7
javascript图灵 - 总结
Node
面试总结
React-Native
Web优化
基础
AngularJS
拍摄
Flutter
Dart
Docker
Linux
mysql
PVE
登录
/
注册
Search
标签搜索
vue+elementui
Cicada
累计撰写
158
篇文章
累计收到
57
条评论
首页
栏目
web前端
Vue
CSS
javascript
React
那些年爬过过的坑
ES6
TypeScrippt
ES7
javascript图灵 - 总结
Node
面试总结
React-Native
Web优化
基础
AngularJS
拍摄
Flutter
Dart
Docker
Linux
mysql
PVE
页面
统计
墙纸
留言
搜索到
6
篇与
的结果
2024-03-27
TypeScript 内置工具详谈
前言TypeScript 提供了几种实用程序类型来助力常见的类型转换。这些实用程序是全局可用的。也就是说全局声明了一些Type, 调用Type就可以方便地进行一些类型转换或者创建新的类型。 不会这些函数一样能写TypeScript你不会真的就不看下文了吧?, 但是掌握后能让你写TypeScript事半功倍。 且掌握这些内置Type是十分必要的。本文章主要对一些比较少用或者难理解的类型做了比较详细的说明。比如 ThisType<T> 等1、Partial 将一个类型的属性全部变为可选定义type Partial<T> = { [P in keyof T]?: T[P]; };从上面的代码中可以看出来该Type使用时需要传入一个泛型T。内部遍历T的所有属性然后创建一个新的 Type,新的Type的所有属性使用 ? 标识,使之为可选。keyof会遍历一个Interface的所有属性名称(key), 生成一个联合类型 "name" | "age" ...,然后可以得到下面代码P in "name" | "age" 这就很明白能看出来了,表明了P为右侧类型使用案例interface UserInfo { name:string; age:number; } // 这里会将 UserInfo 所有的属性变为可选 const foo:Partial<UserInfo> = { name:"张三" }2、Required 将一个类型的属性全部变为必选定义type Required<T> = { [P in keyof T]-?: T[P]; };该Type和Partial刚好是相反的。 从上面的代码中可以看出来该Type实用时需要传入一个泛型T。内部使用-?将T的每个属性去除可选标识使之变成为必填。使用案例interface UserInfo { name?:string; age?:number; } // 这里会将 UserInfo 所有可选的属性变为必选 const foo:Required<UserInfo> = { name:"张三", age:18 }3、Readonly 将一个类型的属性全部变为只读状态定义type Readonly<T> = { readonly [P in keyof T]: T[P]; };从上面的代码中可以看出来该Type实用时需要传入一个泛型T。内部使用readonly将T的每个属性去除可选标识使之变成为只读。使用案例interface UserInfo { name?:string; age?:number; } const foo:Readonly<UserInfo> = { name:"张三", age:18 } foo.name = '李四';// error: 无法分配到 "name" ,因为它是只读属性4、Record 构造一个字面量对象 Type定义type Record<K extends keyof any, T> = { [P in K]: T; };Record 用于方便地构造一个字面量对象。其作用和 { [propName:string]:any } 有些许类似。Record 只需要传入两个 Type 即可创建一个新的 Type,相比于 { [propName:string]:any } 能方便一些。当然除了方便外功能也比它强大,因为Record第一个参数可接收一组key,这样就可以做到定义出一个完整的 Type 了。使用案例// 这是通过 interface 定义出来的。 interface UserInfo { name:string; age:number; } // 我们用 Record 来实现一遍 UserInfo 。 // 注意:后面一个形参和 UserInfo 的是不一样的,因为 Record 第二个参数只能接受一个类型。所以这里要么用 any,要么用这种联合类型。 type UserInfoT = Record<"name" | "age", string | number> // 结果 // type UserInfoT = { // name:string | number; // age:string | number; // }5、Pick 从一个 Type 中选取一些属性来构造一个新的对象 Type定义type Pick<T, K extends keyof T> = { [P in K]: T[P]; };Pick 也用于方便地构造一个字面量对象。其作用和 Record 有些许类似。使用案例interface UserInfo { name:string; age:number; } // 这时候我们只需要 UserInfo 的 name 属性。 type UserInfoT = Pick<UserInfo, "name">6、Omit 从一个对象类型中删除一些属性来构造一个新的对象 Type定义type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>;日常使用中Omit 是一个使用频率可能比较高的。和 Pick 刚刚相反,用于排除不需要的属性。使用案例interface UserInfo { name:string; age:number; } // 这时候我们不需要 UserInfo 的 name 属性。 type UserInfoT = Omit<UserInfo, "name">7、Exclude 排除一个联合类型中的某一些类型来构造一个新 Type定义type Exclude<T, U> = T extends U ? never : T;上面说的 Omit 和 Pick 都是对一个字面量对象 Type 的操作。如果要对一个联合类型操作的话需要用到 Exclude 和 Extract使用案例// 排除掉 "name" type UserInfoT = Exclude<"name" | "age", "name">; // 等价于 type UserInfoA = "age";8、Extract 提取出一个联合类型中的某一些类型来构造一个新 Type定义type Extract<T, U> = T extends U ? T : never;和 Exclude 恰好相反。使用案例// 从 T1 中 提取出 T2 type T1 = "name" | "age" | "hob"; type T2 = "name" | "age"; type UserInfoT = Extract<T1, T2>; // 等价于 type UserInfoA = "name" | "age";既然是提出哪为啥不直接用定义好的 T2?因为这样可以保证 UserInfoT 的类型一定是在 T1 中存在的;9、NonNullable 从类型中排除 null 和 undefined 来构造一个新的 Type定义type NonNullable<T> = T extends null | undefined ? never : T;使用案例// 从 UserInfoK 中 排除掉 null | undefined type UserInfoK = NonNullable<"name" | "hob" | undefined>; // 等价于 type UserInfoKA = "name" | "hob";10、Parameters 从 [函数 Type] 的形参构造一个数组 Type定义type Parameters<T extends (...args: any) => any> = T extends (...args: infer P) => any ? P : never;infer标识一个待推导类型,上面定义的意思是:如果 T 为函数类型,那就返回函数的形参。ps: infer和变量似的,先定义一个 infer P 然后 Ts 就会自动推导函数的形参或者返回值、或者数组元素等,然后开发者在合适的位置使用定义好的infer P即可。一个简单的infer案例。加入有这样一个需求:需要将数组类型的 Type 变为联合类型。其他类型的则不变。这样我们就可以写一个这样的 Typetype ArrayToUnion<T> = T extends Array<infer Item> ? Item : T; const a:ArrayToUnion<[string, number]> = "111"; // a: string | number const b:ArrayToUnion<string | number> = "111"; // a: string | number从这个案列的a变量可以看出作用,a变量的类型定义为ArrayToUnion<[string, number]>,这里传入的是个数组[string, number]被ArrayToUnion处理为了string | number。使用案例// 定义一个函数 function getUserInfo(id:string, group:string){} // 获取到函数需要的形参 Type[] type GetUserInfoArg = Parameters<typeof getUserInfo>; const arg:GetUserInfoArg = [ "001", "002" ]; getUserInfo(...arg);ps: 上面代码中的typeof是 ts 提供的操作符不是 js 中的那个typeof,只能用到 ts 的类型定义中, 所以使用typeof getUserInfo才能指向函数Type11、ConstructorParameters 从定义的[构造函数]的形参构造数组 Type定义type ConstructorParameters<T extends abstract new (...args: any) => any> = T extends abstract new (...args: infer P) => any ? P : never;实现原理完全和 Parameters 一样,只不过这个方法接受的事一个类。使用案例class User{ constructor(id:string, group:string){} } type NewUserArg = ConstructorParameters<typeof User>; const arg:NewUserArg = [ "001", "002"]; new User(...arg);12、ReturnType 用函数 Type 的返回值定义一个新的 Type定义type ReturnType<T extends (...args: any) => any> = T extends (...args: any) => infer R ? R : any;使用 infer 实现。比 Parameters 更简单,可以去看上面的 Parameters 就能明白这段代码意思。使用案例// 定义一个函数 Type type GetUserInfo = ()=>string; const rt:ReturnType<GetUserInfo> = 'xxx'; 13、InstanceType 从一个构造函数的实例定义一个新的 Type定义type InstanceType<T extends abstract new (...args: any) => any> = T extends abstract new (...args: any) => infer R ? R : any;使用 infer 实现。和ReturnType实现原理完全一样。使用案例// 定义一个函数 Type type GetUserInfo = ()=>string; const rt:ReturnType<GetUserInfo> = 'xxx'; 14、ThisParameterType 提取函数 Type 的 this 参数生成一个新的 Type定义type ThisParameterType<T> = T extends (this: infer U, ...args: any[]) => any ? U : unknown;从上面定义看出该 Type 对函数的第一个形参 this 做了infer推导。然后返回了推导出来的this。 不清楚infer的话,往上翻,去仔细看看Parameters一节的说明。使用案例// 定义一个函数,并且定义函数 this 类型。 function getUserInfo(this:{ name:string }){} const getUserInfoArgThis: ThisParameterType<typeof getUserInfo> = { name:"王" };15、OmitThisParameter 忽略函数 Type 的 this 参数,生成一个新的函数 Type定义type OmitThisParameter<T> = unknown extends ThisParameterType<T> ? T : T extends (...args: infer A) => infer R ? (...args: A) => R : T;这个Type看着略微复杂。咋们拆一下看就会简单很多。首先说明一下这个Type的这些判断都是干嘛的。上面定义意思是:如果传入的T没有this参数就直接返回T,如果有this参数就继续进行判断,第二层判断为:如果T不是函数那也会直接返回T,最后是重新定义了一个函数然后返回。其中使用infer定义了我们所需要的形参和返回值。这里在座的各位可能会在(...args: infer A) => infer R ? (...args: A) => R : T这里产生疑惑。上面的写法会直接把this参数过滤掉,为了证实这点,我们可以实现一下:type NoThis<T> = T extends (...args: infer A) => infer R ? A : T const a:NoThis<typeof getUserInfo>; // a: [id: string]上面代码中我们直接返回了推导的A,得到了形参A的类型。这里面是不会包含this的。使用案例// 定义一个函数 function getUserInfo(this:{ name:string }, id:string){} // 去除 getUserInfo 函数 this 参,然后创建出来了一个新类型 const aaa: OmitThisParameter<typeof getUserInfo> = (id:string)=>{} 16、ThisType 给对象标记 this 接口这个类型在 lib.d.ts 中定义的就是一个{}空标签,所以用的时候往往比较困惑。特别是没注意看到官网上写的必须开启--noImplicitThis时才可以用的时候。就算你看到了,但是你在他们案例中如果不注意的话还是搞不懂,因为官方案例中设置了这个编译规则 // @noImplicitThis: false。noImplicitThis 规则开启后在函数中的this在不定义的情况下不能使用,相当于严格模式,默认情况下noImplicitThis的值为false,除非手动开启,否则ThisType毫无作用。使用案例// 注意这行注释,必须开启才能看出效果 // @noImplicitThis: true type ObjectDescriptor<D, M> = { data?: D; methods?: M & ThisType<D & M>; // 这个对象中的 this 要指向 D & M, 注意这里和 methods?: D & M 表示表示是不一样的,ThisType 只改变对象的 this }; function makeObject<D, M>(desc: ObjectDescriptor<D, M>): D & M { let data: object = desc.data || {}; let methods: object = desc.methods || {}; return { ...data, ...methods } as D & M; } let obj = makeObject({ data: { x: 0, y: 0 }, methods: { moveBy(dx: number, dy: number) { this.x += dx; this.y += dy; }, }, }); obj.x = 10; obj.y = 20; obj.moveBy(5, 5);17、Uppercase 将字符串中的每个字符转换为大写这是对字符串的操作,所有对字符串的操作在 lib.d.ts 中都找不到具体的定义,文档上说是为了提升性能。type MyText = "Hello, world" type A = Uppercase<MyText>; // type A = "HELLO, WORLD"18、Lowercase 将字符串中的每个字符转换为小写type MyText = "Hello, world" type A = Lowercase<MyText>; // type A = "hello, world"19、Capitalize 将字符串中的第一个字符转换为大写type MyText = "hello, world" type A = Capitalize<MyText>; // type A = "Hello, world"20、Uncapitalize 将字符串中的第一个字符转换为小写type MyText = "Hello, world" type A = Uncapitalize<MyText>; // type A = "hello, world"
2024年03月27日
3 阅读
0 评论
0 点赞
2019-06-03
TS的命名空间和装饰器
命名空间以namespace关键值定义命名空间.namespace Obj{ export interface Person{ name:string; age:number; getMsg(name:string):string{} } } new Obj.Serson()装饰器装饰器是一种特殊类型的声明,它可以用在类声明、方法、属性或者参数上。顾名思义,它是用来给附着的主体进行装饰,添加额外的行为。装饰器使用@expression这种形式,expression求值后必须为一个函数,它会在运行时被调用,被装饰的声明信息做为参数传入。类装饰器类装饰器:类装饰器在类声明之前被声明(紧靠着类声明)。类装饰器应用于类构造函数,可以用来监视,修改或替换类定义。 传入一个参数function run(target: any) { return class extends target { type: string = 'Web开发工程师' getType() { console.log(this.type) } } } @run class Person { type: string | undefined; constructor(type: string = "工程师") { this.type = type; } getType() { console.log(this.type) } } new Person().getType()属性装饰器属性装饰器接收两个参数(原型对象,参数)属性装饰器表达式会在运行时当作函数被调用,传入下列2个参数:1、对于静态成员来说是类的构造函数,对于实例成员是类的原型对象。2、成员的名字。function test(params: any) { return function (target: any, attr: any) { debugger target[attr] = params; } } class Person { @test('工人') type: string | undefined; constructor(type: string = "工程师") { } getType() { console.log(this.type) } } new Person().getType()方法装饰器它会被应用到方法的属性描述符上,可以用来监视,修改或者替换方法定义。方法装饰会在运行时传入下列3个参数:1、对于静态成员来说是类的构造函数,对于实例成员类的原型对象。2、成员的名字。3、成员的属性描述符。function test(parems?: any): Function { return function (target: any, name: string, desc: any) { let fn: Function = desc.value; desc.value = (...arr: Array<any>): void => { arr = arr.map(v => String(v)); fn(arr); }; }; } class Person { type: string | undefined; constructor(type: string = "工程师") { } @test() getType( ...arr: Array<any>) { console.log(arr) } } new Person().getType(1,"2",3)方法参数装饰器参数装饰器表达式会在运行时当作函数被调用,可以使用参数装饰器为类的原型增加一些元素数据, 传入下列3个参数: 1、对于静态成员来说是类的构造函数,对于实例成员是类的原型对象。2、方法的名字。3、参数在函数参数列表中的索引。
2019年06月03日
1 阅读
0 评论
0 点赞
2019-05-13
TypeScript多态和抽象类
多态父类定义的一个方法不去实现, 让继承的子类去实现.(多态属于继承)class Person{ name:string; run():void{} } Class Mi extends Person{ super('小米'); run():string{ return this.name+'手机' } } new Mi().run()抽象类typescript中的抽象类: 它是提供给其它类继承的基类, 不直接实例化.用abstract关键字定义抽象类和抽象方法. 抽象类中的抽象方法不包含具体实现并必须在派生类中实现.abstract抽象方法只能放在抽象类中.abstract class Person{ name:string; abstract run():void; } Class Mi extends Person{ super('小米'); run():string{ return this.name+'手机' } } new Mi().run()
2019年05月13日
1 阅读
0 评论
0 点赞
2019-04-22
typescript学习笔记二
函数返回值的类型定义fu(): void{}函数的参数必须定义类型fu(name: string, age: number){}函数的默认值fu(name: string = "小米", age: number = 9) {}接口接口的作用: 在面向对象的编程中, 接口是一种规范的定义, 它定义了行为和动作的规范, 在程序设计里面,接口起定义标准的作用.接口的可选属性, 在属性的名的后面添加一个问号(?).只读属性, 在属性名前添加一个readonly.只读数组类型, readonly Array<数组元素类型>. 注意把一个普通数组赋值给readonlyArray后普通数组也只可读.interface Job { type: string; jobTime?: string; description(): string; } class Mi implements Job { readonly type: string; jobTime: string; constructor(type: string, jobTime: string = "8") { this.type = type; this.jobTime = jobTime; } description() { return this.type + '工作' + this.jobTime + "小时;" } } console.log(new Mi("小米").description());函数类型接口, 用来定义函数的参数列表,和返回值的类型的定义.参数列表使用小括号():返回值的类型. 如:(height:string,width:string):boolean;interface Job { (jobTime: string, type?: string): string; } class Mi { readonly type: string; jobTime: string; constructor(type: string, jobTime: string = "8") { this.type = type; this.jobTime = jobTime; } description: Job = (jobTime: string, type: string) => { this.jobTime = jobTime; return this.type + '工作' + this.jobTime + "小时;"; } } console.log(new Mi("小米").description("9"));可索引的类型, 它具有一个索引签名, 签名的类型只有两种(number,string). 还有相应的索引返回值类型.如:readonly [index:number]:string. (只读索引)interface Job { [index: string]: string; } class Mi { readonly type: string; jobTime: string; constructor(type: string, jobTime: string = "8") { this.type = type; this.jobTime = jobTime; } description(jobTime: string, type?: string): object { this.jobTime = jobTime; let obj: Job = { type: this.type, jobTime: this.jobTime } return obj } } console.log(new Mi("小米").description("9"));
2019年04月22日
0 阅读
0 评论
0 点赞
2019-04-22
typescript 学习笔记一
typescript环境的搭建安装环境npm install -g typescript配置typescript 在项目的更目录下新建一个tsconfig.json 配置如下{ "compilerOptions": { "target": "es5", "noImplicitAny": false, "module": "commonjs", "removeComments": true, "sourceMap": false, "outDir": "Golang/TypeScript/" } //"include":[ // "ts" // ], //"exclude": [ // "js" // ] } // target:编译之后生成的JavaScript文件需要遵循的标准。有三个候选项:es3、es5、es2015。 // noImplicitAny:为false时,如果编译器无法根据变量的使用来判断类型时,将用any类型代替。为true时,将进行强类型检查,无法推断类型时,提示错误。 // module:遵循的JavaScript模块规范。主要的候选项有:commonjs、AMD和es2015。为了后面与node.js保持一致,我们这里选用commonjs。 // removeComments:编译生成的JavaScript文件是否移除注释。 // sourceMap:编译时是否生成对应的source map文件。这个文件主要用于前端调试。当前端js文件被压缩引用后,出错时可借助同名的source map文件查找源文件中错误位置。 // outDir:编译输出JavaScript文件存放的文件夹。 // include、exclude:编译时需要包含/剔除的文件夹。编译ts执行tsc -w 会自动检查变化并自动编译typescript类型约束typescript 是超javascript有着类型的检测 let str :string; //这里定义str必须为一个字符串类型,否则会报错 str = '小米' console.log(str) //数组的类型定义 let arr :number[];//在[]前面定义数组元素的类型 arr = [1,2] console.log(arr) //第二种方式是使用数组泛型,Array<元素类型>: let list: Array<number> = [1, 2, 3]; //第三种方式是使用数组泛型,any类型 let list: any[] = [1, 2, 3]; //元组 let arr :[string,number,boolean]; arr = ['min',2,true] console.log(arr) //any let flag :any = null;//any类型可以赋值任何类型 //vold 某种程度上来说,void类型像是与any类型相反,它表示没有任何类型。 当一个函数没有返回值时,你通常会见到其返回值类型是 void: fn():vold{ alert("TS") } //never never类型表示的是那些永不存在的值的类型。 例如, never类型是那些总是会抛出异常或根本就不会有返回值的函数表达式或箭头函数表达式的返回值类型; 变量也可能是 never类型,当它们被永不为真的类型保护所约束时。 // 返回never的函数必须存在无法达到的终点 function error(message: string): never { throw new Error(message); } // 推断的返回值类型为never function fail() { return error("Something failed"); } // 返回never的函数必须存在无法达到的终点 function infiniteLoop(): never { while (true) { }接口接口的定义 interfaceinterface Person { firstName: string; lastName: string; }接口的作用:用来检测类型function allname(str:Person){ //str必须是Person的类型(接口一般配合类使用) console.log(str.firstName+str.lastName) } allname({firstName:"小",lastName:"米"})类类的定义classclass Name{ //类名必须大写 age:number;//类型必须提前定义 constructor(public firstName, public lastName){ this.age=18 } }接口和类的结合使用interface Person { firstName: string; lastName: string; } class Name{ age:number; constructor(public firstName, public lastName){ this.age=18 } } function allname(str:Person){ console.log(str) } allname(new Name('',''))枚举enum Color {Red, Green=0, Blue}; let ColorName:string = Color[0]; console.log(ColorName);使用enum定义默认是从0开始可以手动指定枚举值也可以通过枚举值获得它的名字Any类型Any类型及任何类型,在编译过程中不会检测类型.知道部分类型比如:let arr:Any[] = ["小米"];类型断言使用尖括号方式//("小米").indexOf('小');as方式//("小米" as string).indexOf('米');总结接口: 用来检测类型.类: 用来生成实例.多类型定义使用 | 隔开.
2019年04月22日
0 阅读
0 评论
0 点赞
2019-04-09
vue-cli集成TypeScript
1- 安装ts-loader和typescriptnpm install --save-dev ts-loader@3 typescriptvue-cle@2.9使用的webpack为3, 所以必须使用ts-loader@32- 修改webpack.base.conf.js配置文件修改webpack.base.conf.js下的entry>app为'./src/main.ts' extensions: ['.js', '.vue', '.json', '.ts', '.tsx']//添加'.ts', '.tsx' 文件引入不用写后缀 rules: [ { test: /\.tsx?$/, loader: 'ts-loader', exclude: /node_modules/, options: { appendTsSuffixTo: [/\.vue$/], } },//添加tsloader加载器 ...其他 ]3- 在src目录下新建一个文件vue-shims.d.ts,用于识别单文件vue内的ts代码declare module "*.vue" { import Vue from "vue"; export default Vue; }4- 在项目根目录下建立TypeScript配置文件tsconfig.json{ "compilerOptions": { "strict": true, "module": "es2015", "moduleResolution": "node", "target": "es5", "allowSyntheticDefaultImports": true, "lib": [ "es2017", "dom" ] } } //"jsx": "preserve" jsx支持5- 修改main.js后缀改为ts, 添加引入文件的的后缀.6- 修改router.js后缀改为ts.7- 测试//下面可以测试是否集成成功,编辑src/components/Hello.vue文件,修改 <script lang="ts"> import Vue, {ComponentOptions} from 'vue' export default { name: 'hello', data() { return { msg: 'this is a typescript project now' } } } as ComponentOptions<Vue> </script>8- 配置官方推荐的,vue-class-component修改ts配置文件,增加以下两项配置"allowSyntheticDefaultImports": true,"experimentalDecorators": true,使用vue-class-component后,初始数据可以直接声明为实例的属性,而不需放入data() {return{}}中,组件方法也可以直接声明为实例的方法,如官方实例,更多使用方法可以参考其官方文档import Vue from 'vue' import Component from 'vue-class-component' // @Component 修饰符注明了此类为一个 Vue 组件 @Component({ // 所有的组件选项都可以放在这里 template: '<button @click="onClick">Click!</button>' }) export default class MyComponent extends Vue { // 初始数据可以直接声明为实例的属性 message: string = 'Hello!' // 组件方法也可以直接声明为实例的方法 onClick (): void { window.alert(this.message) } }问题1- ts 无法识别 requireyarn add @types/webpack-env -D
2019年04月09日
2 阅读
0 评论
0 点赞