TypeScript入门笔记篇一,有哪些学习要点?

2026-05-17 00:332阅读0评论SEO资讯
  • 内容介绍
  • 文章标签
  • 相关推荐

本文共计6033个文字,预计阅读时间需要25分钟。

TypeScript入门笔记篇一,有哪些学习要点?

开发环境搭建指南:

1. 创建项目目录 - 进入目标目录 - 输入命令:`mkdir 项目名` - 输入命令:`cd 项目名` - 运行命令:`npm init`(需自行配置)或 `npm init -y`(使用默认配置)

2. 创建src目录 - 目录结构如下: - utils —— 存放业务相关的可复用方法 - tools —— 存放与业务无关的工具类

开发环境搭建 1.新建目录

进入目录 输入命令 npm init(需要自己配置)或者npm init -y (npm默认配置)

2.创建src目录

目录结构大致如下

utils——存放业务相关的可复用方法

tools——存放跟业务无关的纯工具函数

assets——存放静态资源

api——接口

config——配置文件

与src同级的typings文件夹——存放一些为ts模块辅写的生命文件

build文件夹——存放 webpack配置

3.安装ts相关依赖包

全局安装ts和tslint:npm install typescript tslint -g

输入命令tsc --init 初始化配置

4.安装webpack

npm install webpack webpack-cli webpack-dev-server -D

在build文件夹创建webpack.config.js配置文件

TypeScript入门笔记篇一,有哪些学习要点?

5.配置webpack.config.js

module.exports = { entry:"./src/index.ts", //设置入口文件 output:{ filename:"main.js" //输出编译文件 }, resolve:{ extensions:['.ts','.tsx','.js'],//自动解析文件的拓展,例如以后引入js文件会自动找.js结尾的 }, module:{ rules:[{ test:/\.tsx?$/,//匹配后缀是ts或者tsx的文件 use:'ts-loader', //遇到上述文件就用ts-loader解析 exclude:/node_modules/,//编译的时候不去处理这些文件 }] }, //方便在本地开发调试的时候定位到你的代码 devtool:process.env.NODE_ENV === 'production'?false:'inline-source-map', } 6.传入NODE.ENV参数

在package.json 指令区域传入NODE.ENV参数,可以借助工具cross-env

npm install cross-env -D

//package.json "scripts": { "test": "echo \"Error: no test specified\" && exit 1", "start": "cross-env NODE_ENV=development webpack-dev-server --config ./build/webpack.config.js", }, 7.在webpack.config.js配置开发环境和插件设置

npm install clean-webpack-plugin html-webpack-plugin -D

const HtmlWebpackPlugin = require('html-webpack-plugin') const CleanWebpackPlugin = require('clean-webpack-plugin') module.exports = { ... plugins:[ new CleanWebpackPlugin({ cleanOnceBeforeBuildPatterns: ['./dist'] }), new HtmlWebpackPlugin({ template: './src/template/index.html' }) ], } 8.在程序中安装typescript依赖

虽然全局安装了typescript,但是在程序中需要使用typescript依赖

npm install typescript

9.运行npm start 查看本地环境是否能够正常运行

在src目录下创建example文件夹,存放每个单元的代码例子

创建basic-type.ts,随便写几句正常工作的代码

最后记得在index.ts中引入

import './example/basic-type'; 10.配置打包环境,运行npm run build是否能够正常打包

项目依赖包版本号

"devDependencies": { "clean-webpack-plugin": "^2.0.0", "cross-env": "^5.2.0", "html-webpack-plugin": "^3.2.0", "ts-loader": "^5.3.3", "webpack": "^4.29.6", "webpack-cli": "^3.2.3", "webpack-dev-server": "^3.2.1" }, 基础类型 布尔类型

//声明 let bool: boolean = false; //① let bool:boolean; //② bool = false // 当声明是布尔类型时,一旦赋值其他类型的值就会报错 bool = 123 //提示不能将类型“number”分配给类型“boolean”。 数值类型

let num:number = 123 //num = 'abc' //同理会报错 num = 0b1111011 //二进制123 num = 0o173 //八进制123 num = 0x7b //16进制123 字符串类型

let str: string str = 'abc' str = `数值是${num}` //拼接字符串 console.log(str); 数组类型

假设要定义这样一个数组 [1,2,3]

//写法1 指定数组里面的元素都为number类型 let arr1:number[] arr1 = [1,2,3] //报错 arr1 = [1,'2'] //不能将类型“string”分配给类型“number”。 //写法2 指定数组类型,而且里面的元素都为number类型 let arr2:Array<number> //在ts中有一个高级数据类型叫做联合类型,可以指定变量为多种类型 //既可以是string也可以是number类型元素 let arr3:(string|number)[] //写法一 let arr4:Array<string|number> //写法二 arr3 = [1,'abc'] arr4 = [1,'abc'] 元祖类型

//元祖类型 (固定长度,固定位置上的类型) let tuple:[string,number,boolean] tuple = ['a',1,false] //必须一一对应 枚举类型

//枚举类型(序列号可以是自动也可以是自定义) enum Roles{ SUPER_ADMIN, ADMIN = 4, USER //如未定义,序号会自动根据前面的值+1 } //通过名字取得索引值 console.log(Roles.SUPER_ADMIN,Roles.ADMIN,Roles.USER); //0 4 5 //可以通过索引值取得对应的名字 console.log(Roles[0]); //SUPER_ADMIN console.log(Roles[4]); // ADMIN any类型

//any类型,想赋什么类型的值都可以 let value:any value = 'abc' value = 123 value = false const arr:any[] = [1,'abc']

变量设置了any类型,就失去了Typescript的类型安全检测。

使用了any类型,Typescript编译器就不清楚哪些操作是被允许,哪些操作是被禁止的,失去了Typescript提供的类型安全方面的好处,这与直接用JavaScript没有什么区别,失去了使用Typescript的意义

void类型

//void类型 什么类型都不是 // 指定返回值类型为void,在js中如果没有指定返回值,默认返回undefined const consoleText = (text:string):void=>{ console.log(text); } //void类型的变量可以赋值undefined和null let v:void v = undefined v = null //这里因为tsconfig.json 设置了严格模式所以会报错,可以暂时关闭严格模式就不会报错了 consoleText('123');//体现了ts的优越性,编写代码的时候指定参数的数据类型就可以提示使用者参数数据类型是否错误 null 和 undefined

// null 和 undefined 在js中是基础数据类型,在ts中既是值也是类型 let u:undefined u = undefined u = 123 //报错 let n:null n = null n = 'abc' //报错 //在非严格检查下,前面的number类型可以赋值为undefined和null,严格检查下就会报错 num = undefined num = null never类型

// never类型 永远不存在的类型 //涉及到抛出错误的和死循环的,它们的返回值就是never类型 const errorFunc = (message:string):never=>{ throw new Error(message) } //errorFunc('abc') const infiniteFunc = ():never=>{ while(true){} } // never类型是任意类型的子类型,可以赋值给其他任何类型 //此时neverVariable是一个never类型的值 let neverVariable = (()=>{ while(true){} })() neverVariable = '123' //会报错,其他类型的值不可以赋值给never类型 num = neverVariable //不会报错,never类型的值可以赋值给其他类型 object

//赋值是赋值对象在内存中的地址引用 let obj = { name:'windzzz' } let obj2 = obj obj2.name = 'wind' console.log(obj); //可以指定参数的类型为对象类型 function getObject(obj:object):void{ console.log(obj); } getObject(123) //报错,数值类型的参数不能赋值给obj类型的参数 getObject(obj2)

以上就是ts中的基础类型。

类型断言

有时候ts的类型检查并不了解一个值的真实类型,类型断言可以帮助开发者设定这个值的真实类型。它就像类型转换,通过特定语法,将某个值强行指定为我们要指定的类型。

在js中可以定义这么一个函数:当参数有length属性时,返回其长度;当参数无length属性就将其转成字符串再返回长度

const getLength = target =>{ if(target.length || target.length === 0){ return target.length }else{ //传入数值类型就先转成字符串再返回长度 return target.toString().length } }

在ts中限制传入的参数类型为string类型和number类型

const getLength = (target:string|number):number=>{ if(target.length || target.length === 0){ return target.length }else{ //传入数值类型就先转成字符串再返回长度 return target.toString().length } }

可以看到,由于ts做了类型判断,提示类型number不存在length属性,导致报错。

这时候就可以通过类型断言,来指定target在某处地方就是一个string类型

// 写法① 尖括号添加类型名<type> // 写法② 使用关键字as xx as type const getLength = (target:string|number):number=>{ if((<string>target).length || (target as string).length === 0){ return (<string>target).length }else{ //传入数值类型就先转成字符串再返回长度 return target.toString().length } } getLength(123)

类型断言缺点

①每次用到target时都需要设定类型断言,这样很麻烦

②如果使用了jsx,只能使用as这种形式的写法

Symbol

Symbol类型也是ts中的基础类型,表示独一无二的值,在ts中使用Symbol的用法与ES6中的使用方法基本一致。

使用前在tsconfig.json文件中加入es6库,还有取消严格模式,方便代码例子简写

/* Language and Environment */ "target": "es2016", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */ "lib": [ "es6" ], // "strict": true, 声明定义

const s1 = Symbol() console.log(s1); //Symbol() const s2 = Symbol() console.log(s2); //Symbol() // s1 === s2 //false,因为始终返回false,在ts中会报错,可以到浏览器输出看到是false const s3 = Symbol('zzz') const s4 = Symbol('zzz') //s3 === s4 //false //在ts中,Symbol值只能传入number|string类型,传入number类型时会先转换成string类型 作为属性名

对象的普通属性可以这样声明和访问

let prop = 'name' const info = { // name:'zzz' [prop] : 'zzz' //[`my${prop}is`]:'zzz' } console.log(info); //{name: 'zzz'} console.log(info.name) //zzz console.log(info['name'])//zzz

symbol值作为属性名,在对象的内部,Symbol 值必须放在方括号内。

const s5 = Symbol('name') const info2 = { [s5]:'zzz', age:18, sex:'f' } console.log(info2); //修改Symbol属性 info2[s5] = '123' console.log(info2); // info2.s5 = '123' //不能这样子访问修改Symbol属性

以下几种方法对象属性遍历 获取不到使用Symbol作为属性名的属性

for(const key in info2){ console.log(key); //只打印了age和sex } console.log(Object.keys(info2)); //["age","sex"] console.log(Object.getOwnPropertyNames(info2));//["age","sex"] console.log(JSON.stringify(info2)); //{"age":18,"sex":"f"}

获取对象中使用Symbol作为属性名的属性

console.log(Object.getOwnPropertySymbols(info2)); //[Symbol(name)] console.log(Reflect.ownKeys(info2)); //返回任意类型 ['age', 'sex', Symbol(name)] 静态方法

Symbol有2个静态方法:Symbol.for()和Symbol.keyFor()

Symbol.for()

Symbol.for()会在全局范围先检查给定的key是否已经存在,如果不存在才会新建一个全局范围的值

全局范围包括:当前的页面,iframe,service worker

const s6 = Symbol.for('zzz') const s7 = Symbol.for('zzz') s6 === s7 //true Symbol.keyFor()

Symbol.keyFor()会返回Symbol.for全局注册的值传入的标识,找不到则返回undefined

const s6 = Symbol.for('zzz') const s8 = Symbol('123') console.log(Symbol.keyFor(s6)); //zzz console.log(Symbol.keyFor(s8)); //undefined 内置的Symbol值

ES6中提供了11种内置Symbol值

Symbol.hasInstance

当对象内部定义了Symbol.hasInstance方法时,若调用instanceof运算符,就会调用这个Symbol.hasInstance方法。

const obj1 = { [Symbol.hasInstance](otherObj){ console.log(otherObj); } } console.log({ a:'a' } instanceof <any>obj1); //{ a:'a' } Symbol.isConcatSpreadable

用于配置某对象作为Array.prototype.concat()方法的参数时是否展开其数组元素,默认为undefined或设置为true时,对象会被扁平化。

let _arr = [1,2]; console.log([].concat(_arr,[3,4])); //数组会被拆开扁平化 [1, 2, 3, 4] //设置Symbol.isConcatSpreadable 为 false _arr[Symbol.isConcatSpreadable] = false console.log([].concat(_arr,[3,4])); //数组不会被拆开扁平化 [[1, 2], 3, 4] Symbol.species

对象的Symbol.species属性用于指向衍生对象的构造函数

class C extends Array { constructor(...args){ super(...args) } //Symbol.species 指定一个衍生对象的构造函数 static get [Symbol.species] (){ return Array } getName(){ return 'zzz' } } const c = new C(1,2,3) const a = c.map(item=>item+1) console.log(a); console.log(a instanceof C); //false 因为Symbol.species将衍生对象的构造函数指向了Array而非C console.log(a instanceof Array); //true Symbol.match

Symbol.match正则表达式用来匹配字符串,当执行 str.match(xxx) 时会调用该方法(如果存在该属性)

let obj3 = { [Symbol.match](string){ console.log(string.length); }, } //参数类型应为string或RegExp,这里使用了类型断言 'abcde'.match(<RegExp>obj3) // 5 Symbol.split

Symbol.split正则表达式用来分割字符串,当执行 str.split(xxx) 时会调用该方法(如果存在该属性)

let obj3 = { [Symbol.match](string){ console.log(string.length); }, [Symbol.split](string){ console.log('split',string.length); }, } //参数类型应为string或RegExp,这里使用了类型断言 'abcde'.split(<RegExp>obj3) //split 5 Symbol.search

Symbol.search正则表达式用来返回被匹配部分在字符串中的索引,当执行 str.search(xxx) 时会调用该方法(如果存在该属性)

let obj3 = { [Symbol.match](string){ console.log(string.length); }, [Symbol.split](string){ console.log('split',string.length); }, [Symbol.search](string){ console.log('search',string.length); }, } //参数类型应为string或RegExp,这里使用了类型断言 'abcde'.search(<RegExp>obj3) //search 5 Symbol.replace

Symbol.replace正则表达式用来替换字符串中匹配的子串,当执行str.replace(xxx)时会调用该方法(如果存在该属性)

let obj3 = { value:'3', [Symbol.match](string){ console.log(string.length); }, [Symbol.split](string){ console.log('split',string.length); }, [Symbol.search](string){ console.log('search',string.length); }, [Symbol.replace](string) { return '123'; } } //参数类型应为string或RegExp,这里使用了类型断言 'abcde'.match(<any>obj3) // 5 'abcde'.split(<any>obj3) //split 5 'abcde'.search(<any>obj3) //search 5 console.log('abcde'.replace('a',obj3.value)); //3bcde Symbol.iterator

Symbol.iterator返回对象的默认迭代器

const _arr2 = [1,2,3] const iterator = _arr2[Symbol.iterator]() console.log(iterator); console.log(iterator.next());//{value: 1, done: false} Symbol.toPrimitive

Symbol.toPrimitive:对象被转为原始类型的值时,会调用这个方法,返回该对象对应的原始类型值。

let obj4:unknown = { [Symbol.toPrimitive](type){ console.log(type); } } // const res = (obj4 as number)++ //自增运算符,所以转换成number类型,输出是number const res = `abc${obj4}` //网上的代码输出是default,但是自己的环境输出是string Symbol.toStringTag

Symbol.toStringTag返回创建对象时默认的字符串描述,这个属性可以用来定制 [object Object][object Array]object 后的字符串。当执行obj.toString()时调用该方法(如果存在该属性)

let obj5 = { [Symbol.toStringTag]:'zzz' } let obj6 = { get [Symbol.toStringTag](){ return 'zzz' } } console.log(obj5.toString()); //[object zzz] console.log(obj6.toString()); //[object zzz] Symbol.unscopables

Symbol.unscopableswith命令有关。 with将某个对象添加到作用域链的顶部,对象使用Symbol.unscopables时,其对象自身和继承的会被with作用域排除在外。

ts在正常模式下就禁止使用with语句,因为with程序块中的所有符号都具有类型"any"。

//在浏览器使用with const obj7 = { a:'a', b:'b' } with(obj7){ console.log(a); //a console.log(b); //b } console.log(Array.prototype[Symbol.unscopables]); //返回一个对象,可以看到 //at: true //copyWithin: true //entries: true //fill: true //find: true //findIndex: true //findLast: true //findLastIndex: true //flat: true //flatMap: true //includes: true //keys: true //values: true //所以在with中使用上述属性会报错 const arr = [1,2] with(arr){ console.log(findIndex(1)) //Uncaught ReferenceError: findIndex is not defined } //filter不在Array.prototype[Symbol.unscopables]里面,所以可以在with中使用 with(arr){ console.log(filter(item=>item === 1)) //[1] } 接口

ts经常对数据的结构进行检测,接口可以为代码定义一些结构,让使用者能够遵循规则。

引入tslint,使用tslint --init命令初始化tslint.json文件,可以配置tslint的规则。(也可以选择不引用,不影响...)

引入tslint后,就会发现一堆红的报错T^T

在example文件夹创建interface.ts用于学习编写接口例子,记得在index.ts中引入

// import './example/basic-type'; // import './example/symbol'; import './example/interface';

由于引入了tslint,所以在import时如果发现有tslint错误,在tslint.json中设置该规则。(我的tslint版本为6.1.3,所以上述这样写没有报错)

"rules": { "quotemark":false, "semicolon":false, "no-console": false //设置允许使用console }, 基本用法

首先定义一个获得完整名字的方法

const getFullName = ({ firstName, lastName })=>{ return `${firstName} ${lastName}` }

使用该函数,传入参数,可以看到传入名字为number类型不会报错但是并不是想要的结果。

console.log(getFullName({ firstName:'zzz', lastName:123 }));

如果想限制参数对象的类型,可以用之前的写法指定

const getFullName = ( { firstName, lastName }:{ firstName:string,lastName:string } ):string =>{ return `${firstName} ${lastName}` }

使用接口限制传入的对象结构

interface NameInfo { firstName:string, lastName:string } //调用方法时如果没有按照接口规则就会报错 getFullName({ firstName:'haha', // lastName:123 // 设置为number类型时会报错 lastName : '123' }) 可选属性

如果想定义获取蔬菜信息的方法:当蔬菜有颜色时就打印出颜色,没有就是一个空字符串

使用接口

interface Vegetable{ color?:string, //可选属性 type:string, } const getVegetables = ( { color,type }:Vegetable )=>{ return `A ${ color ? (color + ' '):''}${type}` } console.log(getVegetables({ type:'tomato' })); 多余属性检查

如果在定义对象时,定义了接口没有规定的属性,则会有多余属性检查的报错

console.log(getVegetables({ type:'tomato', size:1 //对象文字可以只指定已知属性,并且“size”不在类型“Vegetable”中 })); 绕过多余属性检查

  1. 类型断言
    通过类型断言,强制设定对象就是符合接口规定的。

    console.log(getVegetables({ type:'tomato', size:1 } as Vegetable));

  2. 索引签名
    在接口中添加字符串索引

    interface Vegetable{ color?:string, [prop:string]:any } console.log(getVegetables({ type:'tomato', size:1 }));

  3. 类型兼容性
    类型兼容性用于确定一个类型是否能赋值给其他类型。如果对象a想要赋值给对象b,那么对象b需要的属性,a对象必须都有,对象a中多余的属性不造成影响。

    interface Vegetable{ color?:string, [prop:string]:any } const vegetableInfo = { type:'tomato', // color:'red', size:2 } console.log(getVegetables(vegetableInfo));

设置只读属性

在接口中使用readonly可以使接口属性是只读不可修改的。

interface Vegetable{ color?:string, readonly type:string, } let vegetableObj:Vegetable = { type:'tomato' } vegetableObj.type = 'carrot'; // 报错,无法分配到 "type" ,因为它是只读属性。 函数类型

对函数传入的参数以及返回值进行约束。

interface addFunc{ ( num1:number,num2:number ):number } // 如果是上述这样定义了函数的参数类型和返回值类型,ts会推荐使用类型别名的形式 type addFunc = ( num1:number,num2:number ) =>number const add : addFunc = (n1,n2)=>n1+n2 索引类型

可以给索引/属性名指定类型

interface RoleDic { [id:number]:string } const role:RoleDic = { // 'a':'super_admin' // 报错 0:'super_admin' } interface RoleDic2 { [id:string]:string } const role2:RoleDic2 = { a:'super_admin', 1:'admin' // 不会报错,原因是会自动将number类型先转成string类型 } 继承接口

接口的继承,提高接口的复用性

interface Vegetables { color:string } interface Tomato extends Vegetables{ radius:number } interface Carrot extends Vegetables{ length:number } const tomato:Tomato = { //需要定义Tomato接口中的属性(包括继承的)radisu和color,缺少一个都会报错 radius:1, color:'red' } const carrot : Carrot = { //需要定义carrot接口中的属性(包括继承的)length和color,缺少一个都会报错 length:1, color:'orange' } 混合类型接口

混合类型接口可以实现给函数添加属性

interface Counter { ():void, // 空参数,没有返回值 count:number } const getCounter = (): Counter =>{ const c = ()=>{c.count++} c.count = 0 return c } const counter:Counter = getCounter() counter() console.log(counter.count); // 1 counter() console.log(counter.count); // 2 counter() console.log(counter.count); // 3 函数 函数定义类型

函数定义类型包括对参数返回值的类型定义。

//es5函数写法 function add(arg1:number,arg2:number):number{ return arg1 + arg2 } //es6函数写法 const add = (arg1:number,arg2:number) => arg1 + arg2 完整的函数类型

一个完整的函数类型包括定义函数名、参数、逻辑和返回值。

//ts // 变量存储函数 let add: (x:number,y:number)=>number add = (arg1:number,arg2:number):number =>arg1+arg2 add = (arg1:string,arg2:number)=>arg1+arg2 //error 类型别名

//使用类型别名 type Add = (x:number,y:number)=>number let addFunc:Add addFunc = (arg1:number,arg2:number):number =>arg1+arg2 可选参数

可选参数必须在必选参数后面

type AddFunction = (arg1:number,arg2:number,arg3?:number)=>number let addFunction : AddFunction addFunction = (x:number,y:number)=>x+y addFunction = (x:number,y:number,z:number)=>x+y+z 参数默认值

let addFunction = ( x : number, y = 3)=> x + y // ts会根据赋值判断参数的类型 console.log(addFunction(1));//4 console.log(addFunction(1,5)); //6 剩余参数

//es5写法 这个不能在ts中使用 function handleData(){ if(arguments.length === 1) return arguments[0]*2 else if (arguments.length === 2) return arguments[0]+arguments[1] else return Array.prototype.slice.apply(arguments).join('_') // 将arguments先转换为Array类型,再使用join拼接成字符串 } // es6 const handleData2 = (...args)=>{ console.log(args); } console.log(handleData2(2,4)); // ts const handleData3 = (arg1:number,...args:number[])=>{ } 重载

TypeScript的函数重载通过为一个函数指定多个函数类型定义,从而对函数调用的返回值进行检查

function handleData(x:string):string[] // 函数重载 function handleData(x:number):number[] // 函数重载 function handleData(x:any):any{ // 函数实体 if(typeof x === 'string'){ return x.split('') }else{ return x.toString().split('').map((item)=>Number(item)) } } console.log(handleData('abc')); //['a', 'b', 'c'] console.log(handleData(123)); //[1, 2, 3] 泛型 泛型变量

场景:生成以times为元素个数(默认为5),元素值都为value的数组

const getArray = (value:any,times:number = 5):any[]=>{ return new Array(times).fill(value) // 生成以times为元素个数(默认为5),元素值都为value的数组 }

使用时,发现丢失了类型检测

console.log(getArray(2,3)); // [2, 2, 2] console.log(getArray(5)); // [5, 5, 5, 5, 5] console.log(getArray(5,4).map((item)=>item.length)); //value是any 丢失了类型的检测 这里不会报错,number类型没有length,[undefined, undefined, undefined, undefined]

解决方法:加入泛型变量,告知传入的value是什么类型。

const getArray = <T>(value:T,times:number = 5):T[]=>{ return new Array(times).fill(value) // 生成以times为元素个数(默认为5),元素值都为value的数组 } console.log(getArray<number>(5,4).map((item)=>item.length)); //报错 提示类型“number”上不存在属性“length”

多个泛型变量的使用:

//返回值是元祖类型 const getArray = <T,U>(param1:T,param2:U,times:number):[T,U][]=>{ return new Array(times).fill([param1,param2]) } console.log(getArray<number,string>(1,'a',3)); // [[1, 'a'],[1, 'a'], [1, 'a']] 泛型函数

//简单定义 let getArray:<T>(arg:T,time:number)=>T[] getArray = (arg:any,times:number)=>{ return new Array(times).fill(arg) } getArray(123,3).map((item)=>item.length) //error // 类型别名 + 泛型 type GetArray = <T>(args:T,times:number)=>T[] let getArray:GetArray = (arg:any,times:number)=>{ return new Array(times).fill(arg) } //接口 + 泛型 //可以把接口中泛型变量提升到接口最外层 , 这样接口中所有属性和方法都能使用这个泛型变量了 interface Arr<T> { (arg:T,times:number):void, array:T[] } const getArr = ():Arr<number>=>{ const c = (arg,times)=>{ c.array = new Array(times).fill(arg) }; c.array = [] return c } const getarr:Arr<number> = getArr() getarr(3,4) console.log(getarr.array); // [3, 3, 3, 3] 泛型约束

在使用泛型时,就意味着这个类型是任意的类型。

场景一:对传入的泛型有要求,传入带有length属性的类型

interface ValueWithLength{ length:number } const getArray = <T extends ValueWithLength>(arg:T,times:number):T[]=>{ return new Array(times).fill(arg) } getArray([1,2],3) // getArray(123,3) // 报错 getArray({ length:2 },3) getArray({ length:'3' },3) //报错

泛型变量T受到约束。它必须满足接口ValueWithLength,也就是不管它是什么类型,都必须有一个length属性,且类型为数值类型。

场景二:

const getProps = (object,propName)=>{ return object[propName] } const objs = { a:'a', b:'b' } getProps(objs,'a') getProps(objs,'c') // 不会报错,但实际没有c属性;

可以看到以上代码,在获取objs属性时,若传入objs中不存在的属性,并不会报错,需要加入泛型约束报错提示该属性不存在。

// keyof T 返回对象上所有的属性名构成的一个数组 const getProps = <T,K extends keyof T>(object:T,propName:K)=>{ return object[propName] } const objs = { a:'a', b:'b' } getProps(objs,'a') getProps(objs,'c') // 加了泛型约束后就报错了

本文共计6033个文字,预计阅读时间需要25分钟。

TypeScript入门笔记篇一,有哪些学习要点?

开发环境搭建指南:

1. 创建项目目录 - 进入目标目录 - 输入命令:`mkdir 项目名` - 输入命令:`cd 项目名` - 运行命令:`npm init`(需自行配置)或 `npm init -y`(使用默认配置)

2. 创建src目录 - 目录结构如下: - utils —— 存放业务相关的可复用方法 - tools —— 存放与业务无关的工具类

开发环境搭建 1.新建目录

进入目录 输入命令 npm init(需要自己配置)或者npm init -y (npm默认配置)

2.创建src目录

目录结构大致如下

utils——存放业务相关的可复用方法

tools——存放跟业务无关的纯工具函数

assets——存放静态资源

api——接口

config——配置文件

与src同级的typings文件夹——存放一些为ts模块辅写的生命文件

build文件夹——存放 webpack配置

3.安装ts相关依赖包

全局安装ts和tslint:npm install typescript tslint -g

输入命令tsc --init 初始化配置

4.安装webpack

npm install webpack webpack-cli webpack-dev-server -D

在build文件夹创建webpack.config.js配置文件

TypeScript入门笔记篇一,有哪些学习要点?

5.配置webpack.config.js

module.exports = { entry:"./src/index.ts", //设置入口文件 output:{ filename:"main.js" //输出编译文件 }, resolve:{ extensions:['.ts','.tsx','.js'],//自动解析文件的拓展,例如以后引入js文件会自动找.js结尾的 }, module:{ rules:[{ test:/\.tsx?$/,//匹配后缀是ts或者tsx的文件 use:'ts-loader', //遇到上述文件就用ts-loader解析 exclude:/node_modules/,//编译的时候不去处理这些文件 }] }, //方便在本地开发调试的时候定位到你的代码 devtool:process.env.NODE_ENV === 'production'?false:'inline-source-map', } 6.传入NODE.ENV参数

在package.json 指令区域传入NODE.ENV参数,可以借助工具cross-env

npm install cross-env -D

//package.json "scripts": { "test": "echo \"Error: no test specified\" && exit 1", "start": "cross-env NODE_ENV=development webpack-dev-server --config ./build/webpack.config.js", }, 7.在webpack.config.js配置开发环境和插件设置

npm install clean-webpack-plugin html-webpack-plugin -D

const HtmlWebpackPlugin = require('html-webpack-plugin') const CleanWebpackPlugin = require('clean-webpack-plugin') module.exports = { ... plugins:[ new CleanWebpackPlugin({ cleanOnceBeforeBuildPatterns: ['./dist'] }), new HtmlWebpackPlugin({ template: './src/template/index.html' }) ], } 8.在程序中安装typescript依赖

虽然全局安装了typescript,但是在程序中需要使用typescript依赖

npm install typescript

9.运行npm start 查看本地环境是否能够正常运行

在src目录下创建example文件夹,存放每个单元的代码例子

创建basic-type.ts,随便写几句正常工作的代码

最后记得在index.ts中引入

import './example/basic-type'; 10.配置打包环境,运行npm run build是否能够正常打包

项目依赖包版本号

"devDependencies": { "clean-webpack-plugin": "^2.0.0", "cross-env": "^5.2.0", "html-webpack-plugin": "^3.2.0", "ts-loader": "^5.3.3", "webpack": "^4.29.6", "webpack-cli": "^3.2.3", "webpack-dev-server": "^3.2.1" }, 基础类型 布尔类型

//声明 let bool: boolean = false; //① let bool:boolean; //② bool = false // 当声明是布尔类型时,一旦赋值其他类型的值就会报错 bool = 123 //提示不能将类型“number”分配给类型“boolean”。 数值类型

let num:number = 123 //num = 'abc' //同理会报错 num = 0b1111011 //二进制123 num = 0o173 //八进制123 num = 0x7b //16进制123 字符串类型

let str: string str = 'abc' str = `数值是${num}` //拼接字符串 console.log(str); 数组类型

假设要定义这样一个数组 [1,2,3]

//写法1 指定数组里面的元素都为number类型 let arr1:number[] arr1 = [1,2,3] //报错 arr1 = [1,'2'] //不能将类型“string”分配给类型“number”。 //写法2 指定数组类型,而且里面的元素都为number类型 let arr2:Array<number> //在ts中有一个高级数据类型叫做联合类型,可以指定变量为多种类型 //既可以是string也可以是number类型元素 let arr3:(string|number)[] //写法一 let arr4:Array<string|number> //写法二 arr3 = [1,'abc'] arr4 = [1,'abc'] 元祖类型

//元祖类型 (固定长度,固定位置上的类型) let tuple:[string,number,boolean] tuple = ['a',1,false] //必须一一对应 枚举类型

//枚举类型(序列号可以是自动也可以是自定义) enum Roles{ SUPER_ADMIN, ADMIN = 4, USER //如未定义,序号会自动根据前面的值+1 } //通过名字取得索引值 console.log(Roles.SUPER_ADMIN,Roles.ADMIN,Roles.USER); //0 4 5 //可以通过索引值取得对应的名字 console.log(Roles[0]); //SUPER_ADMIN console.log(Roles[4]); // ADMIN any类型

//any类型,想赋什么类型的值都可以 let value:any value = 'abc' value = 123 value = false const arr:any[] = [1,'abc']

变量设置了any类型,就失去了Typescript的类型安全检测。

使用了any类型,Typescript编译器就不清楚哪些操作是被允许,哪些操作是被禁止的,失去了Typescript提供的类型安全方面的好处,这与直接用JavaScript没有什么区别,失去了使用Typescript的意义

void类型

//void类型 什么类型都不是 // 指定返回值类型为void,在js中如果没有指定返回值,默认返回undefined const consoleText = (text:string):void=>{ console.log(text); } //void类型的变量可以赋值undefined和null let v:void v = undefined v = null //这里因为tsconfig.json 设置了严格模式所以会报错,可以暂时关闭严格模式就不会报错了 consoleText('123');//体现了ts的优越性,编写代码的时候指定参数的数据类型就可以提示使用者参数数据类型是否错误 null 和 undefined

// null 和 undefined 在js中是基础数据类型,在ts中既是值也是类型 let u:undefined u = undefined u = 123 //报错 let n:null n = null n = 'abc' //报错 //在非严格检查下,前面的number类型可以赋值为undefined和null,严格检查下就会报错 num = undefined num = null never类型

// never类型 永远不存在的类型 //涉及到抛出错误的和死循环的,它们的返回值就是never类型 const errorFunc = (message:string):never=>{ throw new Error(message) } //errorFunc('abc') const infiniteFunc = ():never=>{ while(true){} } // never类型是任意类型的子类型,可以赋值给其他任何类型 //此时neverVariable是一个never类型的值 let neverVariable = (()=>{ while(true){} })() neverVariable = '123' //会报错,其他类型的值不可以赋值给never类型 num = neverVariable //不会报错,never类型的值可以赋值给其他类型 object

//赋值是赋值对象在内存中的地址引用 let obj = { name:'windzzz' } let obj2 = obj obj2.name = 'wind' console.log(obj); //可以指定参数的类型为对象类型 function getObject(obj:object):void{ console.log(obj); } getObject(123) //报错,数值类型的参数不能赋值给obj类型的参数 getObject(obj2)

以上就是ts中的基础类型。

类型断言

有时候ts的类型检查并不了解一个值的真实类型,类型断言可以帮助开发者设定这个值的真实类型。它就像类型转换,通过特定语法,将某个值强行指定为我们要指定的类型。

在js中可以定义这么一个函数:当参数有length属性时,返回其长度;当参数无length属性就将其转成字符串再返回长度

const getLength = target =>{ if(target.length || target.length === 0){ return target.length }else{ //传入数值类型就先转成字符串再返回长度 return target.toString().length } }

在ts中限制传入的参数类型为string类型和number类型

const getLength = (target:string|number):number=>{ if(target.length || target.length === 0){ return target.length }else{ //传入数值类型就先转成字符串再返回长度 return target.toString().length } }

可以看到,由于ts做了类型判断,提示类型number不存在length属性,导致报错。

这时候就可以通过类型断言,来指定target在某处地方就是一个string类型

// 写法① 尖括号添加类型名<type> // 写法② 使用关键字as xx as type const getLength = (target:string|number):number=>{ if((<string>target).length || (target as string).length === 0){ return (<string>target).length }else{ //传入数值类型就先转成字符串再返回长度 return target.toString().length } } getLength(123)

类型断言缺点

①每次用到target时都需要设定类型断言,这样很麻烦

②如果使用了jsx,只能使用as这种形式的写法

Symbol

Symbol类型也是ts中的基础类型,表示独一无二的值,在ts中使用Symbol的用法与ES6中的使用方法基本一致。

使用前在tsconfig.json文件中加入es6库,还有取消严格模式,方便代码例子简写

/* Language and Environment */ "target": "es2016", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */ "lib": [ "es6" ], // "strict": true, 声明定义

const s1 = Symbol() console.log(s1); //Symbol() const s2 = Symbol() console.log(s2); //Symbol() // s1 === s2 //false,因为始终返回false,在ts中会报错,可以到浏览器输出看到是false const s3 = Symbol('zzz') const s4 = Symbol('zzz') //s3 === s4 //false //在ts中,Symbol值只能传入number|string类型,传入number类型时会先转换成string类型 作为属性名

对象的普通属性可以这样声明和访问

let prop = 'name' const info = { // name:'zzz' [prop] : 'zzz' //[`my${prop}is`]:'zzz' } console.log(info); //{name: 'zzz'} console.log(info.name) //zzz console.log(info['name'])//zzz

symbol值作为属性名,在对象的内部,Symbol 值必须放在方括号内。

const s5 = Symbol('name') const info2 = { [s5]:'zzz', age:18, sex:'f' } console.log(info2); //修改Symbol属性 info2[s5] = '123' console.log(info2); // info2.s5 = '123' //不能这样子访问修改Symbol属性

以下几种方法对象属性遍历 获取不到使用Symbol作为属性名的属性

for(const key in info2){ console.log(key); //只打印了age和sex } console.log(Object.keys(info2)); //["age","sex"] console.log(Object.getOwnPropertyNames(info2));//["age","sex"] console.log(JSON.stringify(info2)); //{"age":18,"sex":"f"}

获取对象中使用Symbol作为属性名的属性

console.log(Object.getOwnPropertySymbols(info2)); //[Symbol(name)] console.log(Reflect.ownKeys(info2)); //返回任意类型 ['age', 'sex', Symbol(name)] 静态方法

Symbol有2个静态方法:Symbol.for()和Symbol.keyFor()

Symbol.for()

Symbol.for()会在全局范围先检查给定的key是否已经存在,如果不存在才会新建一个全局范围的值

全局范围包括:当前的页面,iframe,service worker

const s6 = Symbol.for('zzz') const s7 = Symbol.for('zzz') s6 === s7 //true Symbol.keyFor()

Symbol.keyFor()会返回Symbol.for全局注册的值传入的标识,找不到则返回undefined

const s6 = Symbol.for('zzz') const s8 = Symbol('123') console.log(Symbol.keyFor(s6)); //zzz console.log(Symbol.keyFor(s8)); //undefined 内置的Symbol值

ES6中提供了11种内置Symbol值

Symbol.hasInstance

当对象内部定义了Symbol.hasInstance方法时,若调用instanceof运算符,就会调用这个Symbol.hasInstance方法。

const obj1 = { [Symbol.hasInstance](otherObj){ console.log(otherObj); } } console.log({ a:'a' } instanceof <any>obj1); //{ a:'a' } Symbol.isConcatSpreadable

用于配置某对象作为Array.prototype.concat()方法的参数时是否展开其数组元素,默认为undefined或设置为true时,对象会被扁平化。

let _arr = [1,2]; console.log([].concat(_arr,[3,4])); //数组会被拆开扁平化 [1, 2, 3, 4] //设置Symbol.isConcatSpreadable 为 false _arr[Symbol.isConcatSpreadable] = false console.log([].concat(_arr,[3,4])); //数组不会被拆开扁平化 [[1, 2], 3, 4] Symbol.species

对象的Symbol.species属性用于指向衍生对象的构造函数

class C extends Array { constructor(...args){ super(...args) } //Symbol.species 指定一个衍生对象的构造函数 static get [Symbol.species] (){ return Array } getName(){ return 'zzz' } } const c = new C(1,2,3) const a = c.map(item=>item+1) console.log(a); console.log(a instanceof C); //false 因为Symbol.species将衍生对象的构造函数指向了Array而非C console.log(a instanceof Array); //true Symbol.match

Symbol.match正则表达式用来匹配字符串,当执行 str.match(xxx) 时会调用该方法(如果存在该属性)

let obj3 = { [Symbol.match](string){ console.log(string.length); }, } //参数类型应为string或RegExp,这里使用了类型断言 'abcde'.match(<RegExp>obj3) // 5 Symbol.split

Symbol.split正则表达式用来分割字符串,当执行 str.split(xxx) 时会调用该方法(如果存在该属性)

let obj3 = { [Symbol.match](string){ console.log(string.length); }, [Symbol.split](string){ console.log('split',string.length); }, } //参数类型应为string或RegExp,这里使用了类型断言 'abcde'.split(<RegExp>obj3) //split 5 Symbol.search

Symbol.search正则表达式用来返回被匹配部分在字符串中的索引,当执行 str.search(xxx) 时会调用该方法(如果存在该属性)

let obj3 = { [Symbol.match](string){ console.log(string.length); }, [Symbol.split](string){ console.log('split',string.length); }, [Symbol.search](string){ console.log('search',string.length); }, } //参数类型应为string或RegExp,这里使用了类型断言 'abcde'.search(<RegExp>obj3) //search 5 Symbol.replace

Symbol.replace正则表达式用来替换字符串中匹配的子串,当执行str.replace(xxx)时会调用该方法(如果存在该属性)

let obj3 = { value:'3', [Symbol.match](string){ console.log(string.length); }, [Symbol.split](string){ console.log('split',string.length); }, [Symbol.search](string){ console.log('search',string.length); }, [Symbol.replace](string) { return '123'; } } //参数类型应为string或RegExp,这里使用了类型断言 'abcde'.match(<any>obj3) // 5 'abcde'.split(<any>obj3) //split 5 'abcde'.search(<any>obj3) //search 5 console.log('abcde'.replace('a',obj3.value)); //3bcde Symbol.iterator

Symbol.iterator返回对象的默认迭代器

const _arr2 = [1,2,3] const iterator = _arr2[Symbol.iterator]() console.log(iterator); console.log(iterator.next());//{value: 1, done: false} Symbol.toPrimitive

Symbol.toPrimitive:对象被转为原始类型的值时,会调用这个方法,返回该对象对应的原始类型值。

let obj4:unknown = { [Symbol.toPrimitive](type){ console.log(type); } } // const res = (obj4 as number)++ //自增运算符,所以转换成number类型,输出是number const res = `abc${obj4}` //网上的代码输出是default,但是自己的环境输出是string Symbol.toStringTag

Symbol.toStringTag返回创建对象时默认的字符串描述,这个属性可以用来定制 [object Object][object Array]object 后的字符串。当执行obj.toString()时调用该方法(如果存在该属性)

let obj5 = { [Symbol.toStringTag]:'zzz' } let obj6 = { get [Symbol.toStringTag](){ return 'zzz' } } console.log(obj5.toString()); //[object zzz] console.log(obj6.toString()); //[object zzz] Symbol.unscopables

Symbol.unscopableswith命令有关。 with将某个对象添加到作用域链的顶部,对象使用Symbol.unscopables时,其对象自身和继承的会被with作用域排除在外。

ts在正常模式下就禁止使用with语句,因为with程序块中的所有符号都具有类型"any"。

//在浏览器使用with const obj7 = { a:'a', b:'b' } with(obj7){ console.log(a); //a console.log(b); //b } console.log(Array.prototype[Symbol.unscopables]); //返回一个对象,可以看到 //at: true //copyWithin: true //entries: true //fill: true //find: true //findIndex: true //findLast: true //findLastIndex: true //flat: true //flatMap: true //includes: true //keys: true //values: true //所以在with中使用上述属性会报错 const arr = [1,2] with(arr){ console.log(findIndex(1)) //Uncaught ReferenceError: findIndex is not defined } //filter不在Array.prototype[Symbol.unscopables]里面,所以可以在with中使用 with(arr){ console.log(filter(item=>item === 1)) //[1] } 接口

ts经常对数据的结构进行检测,接口可以为代码定义一些结构,让使用者能够遵循规则。

引入tslint,使用tslint --init命令初始化tslint.json文件,可以配置tslint的规则。(也可以选择不引用,不影响...)

引入tslint后,就会发现一堆红的报错T^T

在example文件夹创建interface.ts用于学习编写接口例子,记得在index.ts中引入

// import './example/basic-type'; // import './example/symbol'; import './example/interface';

由于引入了tslint,所以在import时如果发现有tslint错误,在tslint.json中设置该规则。(我的tslint版本为6.1.3,所以上述这样写没有报错)

"rules": { "quotemark":false, "semicolon":false, "no-console": false //设置允许使用console }, 基本用法

首先定义一个获得完整名字的方法

const getFullName = ({ firstName, lastName })=>{ return `${firstName} ${lastName}` }

使用该函数,传入参数,可以看到传入名字为number类型不会报错但是并不是想要的结果。

console.log(getFullName({ firstName:'zzz', lastName:123 }));

如果想限制参数对象的类型,可以用之前的写法指定

const getFullName = ( { firstName, lastName }:{ firstName:string,lastName:string } ):string =>{ return `${firstName} ${lastName}` }

使用接口限制传入的对象结构

interface NameInfo { firstName:string, lastName:string } //调用方法时如果没有按照接口规则就会报错 getFullName({ firstName:'haha', // lastName:123 // 设置为number类型时会报错 lastName : '123' }) 可选属性

如果想定义获取蔬菜信息的方法:当蔬菜有颜色时就打印出颜色,没有就是一个空字符串

使用接口

interface Vegetable{ color?:string, //可选属性 type:string, } const getVegetables = ( { color,type }:Vegetable )=>{ return `A ${ color ? (color + ' '):''}${type}` } console.log(getVegetables({ type:'tomato' })); 多余属性检查

如果在定义对象时,定义了接口没有规定的属性,则会有多余属性检查的报错

console.log(getVegetables({ type:'tomato', size:1 //对象文字可以只指定已知属性,并且“size”不在类型“Vegetable”中 })); 绕过多余属性检查

  1. 类型断言
    通过类型断言,强制设定对象就是符合接口规定的。

    console.log(getVegetables({ type:'tomato', size:1 } as Vegetable));

  2. 索引签名
    在接口中添加字符串索引

    interface Vegetable{ color?:string, [prop:string]:any } console.log(getVegetables({ type:'tomato', size:1 }));

  3. 类型兼容性
    类型兼容性用于确定一个类型是否能赋值给其他类型。如果对象a想要赋值给对象b,那么对象b需要的属性,a对象必须都有,对象a中多余的属性不造成影响。

    interface Vegetable{ color?:string, [prop:string]:any } const vegetableInfo = { type:'tomato', // color:'red', size:2 } console.log(getVegetables(vegetableInfo));

设置只读属性

在接口中使用readonly可以使接口属性是只读不可修改的。

interface Vegetable{ color?:string, readonly type:string, } let vegetableObj:Vegetable = { type:'tomato' } vegetableObj.type = 'carrot'; // 报错,无法分配到 "type" ,因为它是只读属性。 函数类型

对函数传入的参数以及返回值进行约束。

interface addFunc{ ( num1:number,num2:number ):number } // 如果是上述这样定义了函数的参数类型和返回值类型,ts会推荐使用类型别名的形式 type addFunc = ( num1:number,num2:number ) =>number const add : addFunc = (n1,n2)=>n1+n2 索引类型

可以给索引/属性名指定类型

interface RoleDic { [id:number]:string } const role:RoleDic = { // 'a':'super_admin' // 报错 0:'super_admin' } interface RoleDic2 { [id:string]:string } const role2:RoleDic2 = { a:'super_admin', 1:'admin' // 不会报错,原因是会自动将number类型先转成string类型 } 继承接口

接口的继承,提高接口的复用性

interface Vegetables { color:string } interface Tomato extends Vegetables{ radius:number } interface Carrot extends Vegetables{ length:number } const tomato:Tomato = { //需要定义Tomato接口中的属性(包括继承的)radisu和color,缺少一个都会报错 radius:1, color:'red' } const carrot : Carrot = { //需要定义carrot接口中的属性(包括继承的)length和color,缺少一个都会报错 length:1, color:'orange' } 混合类型接口

混合类型接口可以实现给函数添加属性

interface Counter { ():void, // 空参数,没有返回值 count:number } const getCounter = (): Counter =>{ const c = ()=>{c.count++} c.count = 0 return c } const counter:Counter = getCounter() counter() console.log(counter.count); // 1 counter() console.log(counter.count); // 2 counter() console.log(counter.count); // 3 函数 函数定义类型

函数定义类型包括对参数返回值的类型定义。

//es5函数写法 function add(arg1:number,arg2:number):number{ return arg1 + arg2 } //es6函数写法 const add = (arg1:number,arg2:number) => arg1 + arg2 完整的函数类型

一个完整的函数类型包括定义函数名、参数、逻辑和返回值。

//ts // 变量存储函数 let add: (x:number,y:number)=>number add = (arg1:number,arg2:number):number =>arg1+arg2 add = (arg1:string,arg2:number)=>arg1+arg2 //error 类型别名

//使用类型别名 type Add = (x:number,y:number)=>number let addFunc:Add addFunc = (arg1:number,arg2:number):number =>arg1+arg2 可选参数

可选参数必须在必选参数后面

type AddFunction = (arg1:number,arg2:number,arg3?:number)=>number let addFunction : AddFunction addFunction = (x:number,y:number)=>x+y addFunction = (x:number,y:number,z:number)=>x+y+z 参数默认值

let addFunction = ( x : number, y = 3)=> x + y // ts会根据赋值判断参数的类型 console.log(addFunction(1));//4 console.log(addFunction(1,5)); //6 剩余参数

//es5写法 这个不能在ts中使用 function handleData(){ if(arguments.length === 1) return arguments[0]*2 else if (arguments.length === 2) return arguments[0]+arguments[1] else return Array.prototype.slice.apply(arguments).join('_') // 将arguments先转换为Array类型,再使用join拼接成字符串 } // es6 const handleData2 = (...args)=>{ console.log(args); } console.log(handleData2(2,4)); // ts const handleData3 = (arg1:number,...args:number[])=>{ } 重载

TypeScript的函数重载通过为一个函数指定多个函数类型定义,从而对函数调用的返回值进行检查

function handleData(x:string):string[] // 函数重载 function handleData(x:number):number[] // 函数重载 function handleData(x:any):any{ // 函数实体 if(typeof x === 'string'){ return x.split('') }else{ return x.toString().split('').map((item)=>Number(item)) } } console.log(handleData('abc')); //['a', 'b', 'c'] console.log(handleData(123)); //[1, 2, 3] 泛型 泛型变量

场景:生成以times为元素个数(默认为5),元素值都为value的数组

const getArray = (value:any,times:number = 5):any[]=>{ return new Array(times).fill(value) // 生成以times为元素个数(默认为5),元素值都为value的数组 }

使用时,发现丢失了类型检测

console.log(getArray(2,3)); // [2, 2, 2] console.log(getArray(5)); // [5, 5, 5, 5, 5] console.log(getArray(5,4).map((item)=>item.length)); //value是any 丢失了类型的检测 这里不会报错,number类型没有length,[undefined, undefined, undefined, undefined]

解决方法:加入泛型变量,告知传入的value是什么类型。

const getArray = <T>(value:T,times:number = 5):T[]=>{ return new Array(times).fill(value) // 生成以times为元素个数(默认为5),元素值都为value的数组 } console.log(getArray<number>(5,4).map((item)=>item.length)); //报错 提示类型“number”上不存在属性“length”

多个泛型变量的使用:

//返回值是元祖类型 const getArray = <T,U>(param1:T,param2:U,times:number):[T,U][]=>{ return new Array(times).fill([param1,param2]) } console.log(getArray<number,string>(1,'a',3)); // [[1, 'a'],[1, 'a'], [1, 'a']] 泛型函数

//简单定义 let getArray:<T>(arg:T,time:number)=>T[] getArray = (arg:any,times:number)=>{ return new Array(times).fill(arg) } getArray(123,3).map((item)=>item.length) //error // 类型别名 + 泛型 type GetArray = <T>(args:T,times:number)=>T[] let getArray:GetArray = (arg:any,times:number)=>{ return new Array(times).fill(arg) } //接口 + 泛型 //可以把接口中泛型变量提升到接口最外层 , 这样接口中所有属性和方法都能使用这个泛型变量了 interface Arr<T> { (arg:T,times:number):void, array:T[] } const getArr = ():Arr<number>=>{ const c = (arg,times)=>{ c.array = new Array(times).fill(arg) }; c.array = [] return c } const getarr:Arr<number> = getArr() getarr(3,4) console.log(getarr.array); // [3, 3, 3, 3] 泛型约束

在使用泛型时,就意味着这个类型是任意的类型。

场景一:对传入的泛型有要求,传入带有length属性的类型

interface ValueWithLength{ length:number } const getArray = <T extends ValueWithLength>(arg:T,times:number):T[]=>{ return new Array(times).fill(arg) } getArray([1,2],3) // getArray(123,3) // 报错 getArray({ length:2 },3) getArray({ length:'3' },3) //报错

泛型变量T受到约束。它必须满足接口ValueWithLength,也就是不管它是什么类型,都必须有一个length属性,且类型为数值类型。

场景二:

const getProps = (object,propName)=>{ return object[propName] } const objs = { a:'a', b:'b' } getProps(objs,'a') getProps(objs,'c') // 不会报错,但实际没有c属性;

可以看到以上代码,在获取objs属性时,若传入objs中不存在的属性,并不会报错,需要加入泛型约束报错提示该属性不存在。

// keyof T 返回对象上所有的属性名构成的一个数组 const getProps = <T,K extends keyof T>(object:T,propName:K)=>{ return object[propName] } const objs = { a:'a', b:'b' } getProps(objs,'a') getProps(objs,'c') // 加了泛型约束后就报错了