2019年,typescript注定成为一个很热门的前端技术,因为vue3.0用typescript来写整个框架,很多框架和库都用了typescript,deno是不是说你?还有react很火的ui框架antd-design也用typescript,赶紧抓紧typescript吧!
这可能是目前为止最全最好学习的typescirpt教程,持续更新中,代码和文档都在github中,欢迎github star。
为什么要学习和使用TypeScript?
TypeScript是一种纯面向对象的语言。相比JavaScript,它更能提高我们的开发效率。而且,由于TypeScript是JavaScript的超集,TypeScript能够和JavaScript混合使用。因为TypeScript的这个强大特性,越来越多的优秀框架开始尝试使用TypeScript。
在编写代码的阶段,TypeScript就能够找到大部分的错误,而JavaScript在这方面就没那么友好了。要知道,运行时错误越少,你的程序的bug就越少。除此之外,相比JavaScript,TypeScript的重构也更容易。
供参考文档:
TypeScript预览
-
3: TypeScript函数
-
4: TypeScript类
-
5: TypeScript接口
-
6: TypeScript泛型
一、TypeScript介绍、安装和编译
TypeScript 介绍
1.TypeScript 是由微软开发的一款开源的编程语言,像后端 java、C#这样的面向对象语言可以让 js 开发大型企业项目。
2.TypeScript 是 Javascript的超级,遵循最新的 ES6、Es5 规范(相当于包含了es6、es5的语法)。TypeScript扩展了JavaScript的语法。
3.最新的 Vue 、React 也可以集成 TypeScript。
供参考文档:
TypeScript安装编译
- 安装nodejs环境,用npm全局安装typescript编译器
npm install -g typescript
复制代码
- typescript手动编译
typescript文件后缀名为.ts,最后将编译成js文件。ts是js的扩展,想要ts代码在浏览器/Node环境下运行,需要把ts代码编译成js代码。
npm安装typescript后,命令行输入tsc + 文件名就可以把ts编译成js。
在index.ts中:
console.log('hello typescript')
复制代码
在命令行上,运行typescript编译器:
tsc index.ts
复制代码
输出结果为一个index.js文件,它包含了和输入文件中相同的javascript代码。因为typescript是javascript的超集,完全兼容javascript。
TypeScript自动编译
上面的手动编译ts很麻烦,配置开发编辑工具Visual Studio Code(Vscode),就可以自动编译ts。
- 命令行输入tsc --init,会生成配置文件tsconfig.json,使用默认配置即可。
- tsconfig.json详细配置在官网
- 在Vscode编辑器中点击菜单栏-任务-运行任务(遇到错误使用快捷键ctrl + shift + b),点击 tsc:监视-tsconfig.json,在index.ts里面写入js代码并保存,会自动编译成index.js。
二、TypeScript数据类型
typescript中为了使编写的代码更规范,更有利于维护,增加了类型校验(类型声明)
基础类型
在typescript中主要给我们提供了以下数据类型:
- 字符串类型(string)
- 数字类型(number)
- 布尔类型(boolean)
- null和undefined
- 数组类型(array)
- 元组类型(tuple)
- 枚举类型(enum)
- 任意类型(any)
- void类型
- never类型
相比于js的数据类型,typescript中多了元组类型、枚举类型、任意类型、void类型和never类型。当然这些只是基础类型,还有更多其他类型,后面的类型推论和高级类型可以进一步了解。
变量定义
写ts代码变量可以指定其类型,指定类型后赋值必须为指定的类型,否则报错。
- 如果没有指定类型,ts类型推论会帮助提供类型,请看ts类型推论。
var flag:boolean = true
flag = 123 // 错误,类型不一致
复制代码
数据类型
字符串类型(string)
var str:string = 'this is ts';
str='haha'; //正确
// str=true; //错误
复制代码
数字类型(number)
var num:number = 123;
num = 456; // 正确
// num='str'; //错误
复制代码
布尔类型(boolean)
var flag:boolean = true
flag = false // 正确
// flag=123; // 错误
复制代码
null 和 undefined
undefined:
{
// 在js中,变量已声明但未初始化为undefined
var undefinedTest:number
// console.log(undefinedTest) // 错误写法,typescript报错,赋值了才正确
// 在typescript中,已声明未初始化的值要直接访问的话类型需要定义为undefined
var undefinedTest2:undefined
console.log(undefinedTest2) // 正确写法,输出undefined
}
{
// 可能是number类型 可能是undefined
var undefinedTest3:number | undefined;
console.log(num);
}
复制代码
null:
// null是一个空指针对象,undefined是未初始化的变量。因此,可以把undefined看作是空的变量,而null看作是空的对象
var nullTest:null
nullTest = null
// nullTest = {} // 错误,定义了类型是null,值必须为null
复制代码
数组类型(array)
ts有两种方式可以定义数组。 第一种,可以在元素类型后面接上 [],表示由此类型元素组成的一个数组:
// 第一种
var arr:number[] = [1, 2, 3]
复制代码
第二种方式是使用数组泛型,Array<元素类型>:
// 第二种
var arr2:Array<number> = [1, 2, 3]
复制代码
元组类型(tuple)
和数组类似,元素的类型不一样:
let arr:[number,string] = [123,'this is ts']
复制代码
枚举类型(enum)
用法:
enum 枚举名{
标识符[=整型常数],
标识符[=整型常数],
...
标识符[=整型常数],
}
复制代码
enum Flag {success = 1,error = 2};
let s:Flag = Flag.success // 使用枚举类型中的值
console.log('正确状态',s)
let f:Flag = Flag.error
console.log('错误状态',f)
复制代码
任意类型(any)
为那些在编程阶段还不清楚类型的变量指定一个类型
var number:any = 123
number = 'str'
number = true
复制代码
void类型
typescript中的void表示没有任何类型,一般用于定义方法的时候方法没有返回值。
// 表示方法没有返回任何类型
function run(): void {
console.log('run')
}
run()
复制代码
never类型
表示的是那些永不存在的值的类型,例如异常
var a:never
// a = 123 //错误写法
a = (() => {
throw new Error('错误');
})()
复制代码
三、TypeScript函数
内容概述: 函数的定义、可选参数、默认参数、剩余参数、函数重载、箭头函数。
函数的定义
语法:
// 函数声明
function fn(x: Type, y: Type): Type {}
// 函数表达式
var fn = (x: Type, y: Type): Type => {}
// 函数表达式:指定变量fn的类型
var fn: (x: Type, y: Type) => Type = (x, y) => {}
复制代码
- 定义函数有函数声明和函数表达式两种形式。定义函数的参数和返回值可以指定其类型;当调用函数时,传入参数类型必须与定义函数参数类型保持一致。
// 函数声明法
function run(x: number, y: number): number {
return x + y;
}
// 函数表达式法
var run2 = (x: number, y: number): string => {
return 'run2'
}
run(1, 2);
run2(1, 2);
复制代码
这段代码中,函数run和run2指定了参数类型,调用时传入参数类型必须保持一致。
- 函数表达式法另外一种写法
var run3: (x: number, y: number) => string = function(x: number, y: number): string{
return 'run3';
}
run3(1, 2);
复制代码
当给变量run3指定类型的时候,应该是函数的参数和返回值的约束类型。如果用后面学到的ts类型推论,可以简写为:
var run4: (x: number, y: number) => string = function(x, y){ // 类型推论可以确定函数的参数和返回值类型,也就可以省略类型指定
return 'run4';
}
run4(1, 2);
复制代码
- 函数没有返回值用void类型指定返回值类型
function voidFnc(): void{
console.log('没有返回值的方法用void')
}
voidFnc();
复制代码
可选参数
- es5里面方法的实参和行参可以不一样,但是ts中必须一样,如果不一样就需要在可选参数后加?,这就是可选参数。
function electParam(name:string, age?:number):string {
// 这里的age可传可不传,age就是可选参数
if(age){
return `${name} --- ${age}`
}else{
return `${name} --- 年龄保密`
}
}
console.log('可选参数', electParam('dz'))
// 注意: 可选参数必须配置到参数的最后面
// 错误写法:可选参数不在最后面
// function electParam2(name?: string, age: number): string {
// ...
// }
复制代码
默认参数
- es5里面没法设置默认参数,es6和ts中都可以设置默认参数
// age为默认参数
function defaultParam(name:string, age:number = 20):String {
return `${name} --- ${age}`
}
console.log('默认参数', defaultParam('dz'))
复制代码
剩余参数
- 当有很多参数时候或参数个数不确定,可以用三点运算符
// sum参数传过来的是一个数组
function sum(...result: number[]): number {
var sum = 0;
for (var i = 0; i < result.length; i++) {
sum += result[i];
}
return sum;
}
console.log('剩余参数', sum(1, 2, 3, 4, 5, 6));
// a=1 b=2 其他参数为剩余参数
function sum2(a: number, b: number, ...result: number[]): number {
var sum = a * b;
for (var i = 0; i < result.length; i++) {
sum += result[i];
}
return sum;
}
console.log('剩余参数2', sum2(1, 2, 3, 4, 5, 6));
复制代码
函数重载
同名函数,传入不同的参数,实现不同的功能,这就叫作函数重载。
- java中方法的重载:重载指的是两个或者两个以上同名函数,但它们的参数不一样,这时会出现函数重载的情况。
- typescript中的重载:通过为同一个函数提供多个函数类型定义来实现多种功能的目的。
- ts为了兼容es5以及es6,重载的写法和java中有区别。
es5中同名函数,后面会覆盖前面的函数,ts中则不会:
function overloadingFn(x: number, y: number): number;
function overloadingFn(x: string, y: string): string;
// 上面定义函数的格式,下面定义函数的具体实现
function overloadingFn(x: any, y: any): any {
return x + y;
}
overloadingFn(1, 2);
overloadingFn('a', 'b');
复制代码
这段代码中,同名函数overloadingFn首先定义两个函数的格式,然后再去实现功能,原来要传入不同类型参数要用多个函数实现,现在可以用同名函数来实现,这就是函数重载。
箭头函数
箭头函数和es6中一样
setTimeout(() => {
console.log('箭头函数')
}, 1000);
复制代码
四、TypeScript类
es5中的类
内容概述:类的创建、静态方法、继承(对象冒充继承,原型链继承,对象冒充 + 原型链组合继承)
es5中的面向对象、构造函数、原型与原型链本质可以看这个文档caibaojian.com/javascript-… , 个人觉得写得很清晰。
1.1 类的创建
es5类在构造函数和原型链里都可以添加属性和方法,原型链上的属性会被多个实例所共享,而构造函数则不会。
function Person() {
this.name = 'Ming'
this.run = function() {
console.log(this.name + '在运动')
}
}
Person.prototype.sex = '男' // 原型链上的属性会被多个实例所共享
Person.prototype.work = function() {
console.log(this.name + '在工作')
}
var p = new Person()
p.run()
p.work()
console.log(p.name)
复制代码
1.2 静态方法
调用静态方法不需要实例化
Person.getInfo=function(){
console.log('我是静态方法');
}
Person.getInfo();
复制代码
1.3 实现继承
对象冒充(或者叫构造函数继承)继承:可以继承构造函数里面的属性和方法,但是没法继承原型链上面的属性和方法
原型继承:可以继承构造函数里面的属性和方法,也可以继承原型链上面的属性和方法,但是实例化子类的时候没法给父类传参
下面是通过对象冒充 + 原型链组合继承,解决了上面两种继承方式存在的问题
function Worker(name,age){
this.name=name; /*属性*/
this.age=age;
this.run=function(){ /*实例方法*/
alert(this.name+'在运动');
}
}
Worker.prototype.sex="男";
Worker.prototype.work=function(){
alert(this.name+'在工作');
}
function Web(name,age){
Worker.call(this,name,age); // 对象冒充继承,可以继承构造函数里面的属性和方法,实例化子类可以给父类传参
}
// Web.prototype = new Worker(); // 原型链继承方法一:继承Worker构造函数和原型上所有的方法和属性
Web.prototype = Worker.prototype; //原型链继承方法二:优化了方法一重复继承构造函数属性和方法的问题(本质可以看看http://caibaojian.com/javascript-object-5.html)
var w = new Web('赵四',20);
w.run();
w.work();
复制代码
从上面可以看出,对象冒充继承是在子类Web构造函数里面通过call方法继承父类Worker的构造函数的属性和方法;原型链继承通过子类Web的原型对象等于父类Worker的原型对象来实现继承;最后这两种继承的组合方式实现了完美继承。
typescript中的类
内容概述: ts中类的定义、继承、类修饰符、静态属性和静态方法、多态、抽象类和抽象方法
2.1 ts中类的定义
ts中类的定义和es6类的定义一样
class PersonDefine {
name: string // 属性,前面省略了public关键词
constructor(name:string) { //构造函数
this.name = name
}
run():string { // 原型
return `${this.name}在运动`
}
}
var define = new PersonDefine('类的定义')
alert(define.run())
复制代码
2.2 继承
ts中继承比es5简单很多,用extends super实现继承
class WebExtend extends PersonDefine {
constructor(name:string) {
super(name) // super继承父类的构造函数,并向父类构造函数传参
}
work():string {
return `${this.name}在工作`
}
}
var extend = new WebExtend('继承')
alert(extend.run())
alert(extend.work())
复制代码
2.3 ts类里面的修饰符
修饰符:typescript里面定义属性的时候给我们提供了三种修饰符
- public: 公有修饰符,在当前类里面、子类、类外面都可以访问
- protected:保护类型,在当前类里面、子类里面可以访问,在类外部没法访问
- private :私有修饰符,在当前类里面可以访问,子类、类外部都没法访问
注意:属性如果不加修饰符,默认就是公有修饰符
// 以private为例
class PersonPrivate{
private name:string; /*被private修饰的属性 => 私有属性*/
constructor(name:string){
this.name=name;
}
run():string{
return `${this.name}在运动` // 私有属性只能在当前类里面可以访问
}
}
class Web extends PersonPrivate{
constructor(name:string){
super(name)
}
work(){
// return `${this.name}在工作` // 报错,子类不能访问父类的私有属性
}
}
var privateName = new PersonPrivate('private')
alert(privateName.run())
// console.log(privateName.name) // 报错,外部不能访问类的私有属性
复制代码
2.4静态属性和静态方法
为什么要用静态属性和静态方法?jq里面的$.ajax就是用的静态方法
function $(element) {
return new Base(element)
}
function Base(element) {
this.element = document.getElementById(element)
this.css = function(arr, value) {
this.element.style[arr] = value
}
}
$('box').css('color','red')
$.ajax = function() {} // 想要在$上使用方法怎么办,用静态方法
复制代码
ts中实现静态属性和静态方法用static
class PersonStatic{
/*公有属性*/
public name:string;
constructor(name:string) {
this.name=name;
}
/*实例方法(需要被实例化,所以为实例方法)*/
run(){
return `${this.name}在运动`
}
/*静态属性*/
static sex = '男'
/*静态方法,里面没法直接调用类里面的属性*/
static info(){
// return 'info方法' + this.name // 静态方法不能调用本类的方法和属性,可以调用静态属性
return 'info方法' + PersonStatic.sex
}
}
console.log('静态方法' + PersonStatic.info())
console.log('静态属性' + PersonStatic.sex)
复制代码
2.5多态
父类定义一个方法不去实现,让继承它的子类去实现,每一个子类的该方法有不同的表现
- 多态属于继承
比如定义一个父类Animal,里面的eat方法不去实现,让子类Dog和Cat分别实现自己的eat方法
class Animal {
name:string;
constructor(name:string) {
this.name=name;
}
eat(){ // eat方法继承它的子类去实现
}
}
class Dog extends Animal{
constructor(name:string){
super(name)
}
eat(){
return this.name+'吃粮食'
}
}
class Cat extends Animal{
constructor(name:string){
super(name)
}
eat(){
return this.name+'吃老鼠'
}
}
复制代码
2.6抽象类和抽象方法
定义:用abstract关键字定义抽象类和抽象方法,抽象类中的抽象方法不包含具体实现并且必须在派生类(抽象类的子类)中实现
- 抽象类:它是提供其他类继承的基类,不能直接被实例化,子类继承可以被实例化
- abstract修饰的方法(抽象方法)只能放在抽象类里面
- 抽象类和抽象方法用来定义标准(比如定义标准为:抽象类Animal有抽象方法eat,要求它的子类必须包含eat方法)
abstract class AnimalAbst{
public name:string;
constructor(name:string){
this.name=name;
}
abstract eat():any; //抽象方法不包含具体实现并且必须在派生类中实现
run(){
console.log('其他方法可以不实现')
}
}
// var a = new Animal() /*错误的写法,抽象类不能被实例化*/
class DogAbst extends Animal{
//抽象类的子类必须实现抽象类里面的抽象方法
constructor(name:any){
super(name)
}
eat(){
return this.name + '吃粮食'
}
}
var d = new DogAbst('小花花');
console.log('抽象类和抽象方法',d.eat());
复制代码
五、TypesSript接口
接口定义:接口是对传入参数进行约束;或者对类里面的属性和方法进行声明和约束,实现这个接口的类必须实现该接口里面属性和方法;typescript中的接口用interface关键字定义。
接口作用:接口定义了某一批类所需要遵守的规范,接口不关心这些类的内部状态数据,也不关心这些类里方法的实现细节,它只规定这批类里必须提供某些方法,提供这些方法的类就可以满足实际需要。typescrip中的接口类似于java,同时还增加了更灵活的接口类型,包括属性、函数、可索引和类等。
内容概述:接口分类:(属性接口、函数类型接口、可索引接口、类类型接口),接口的继承
1. 接口分类
1.1 属性接口
对传入对象的约束(也就是对json的约束)
在了解接口之前,我们来看看函数传入obj参数
function printLabel(labelInfo: {label:string}){
return labelInfo
}
// printLabel({name:'obj'}); //错误的写法
console.log(printLabel({label: 'obj'}))
复制代码
和上面类似,由此引入属性接口 => 对方法传入参数进行约束
下面为属性接口的例子,方法printFullName对传入参数FullName(为对象)进行约束
interface FullName{
firstName: string; // 注意;结束
secondName: string;
age?: number // 接口的可选属性用?
}
function printFullName(name:FullName) {
// 传入对象必须包含firstName和secondName,可传可不传age
return name
}
var obj = {
firstName:'小',
secondName:'明',
age: 20
}
console.log(printFullName(obj))
复制代码
属性接口应用:原生js封装ajax
interface Config{
type: string;
url: string;
data?: string;
dataType: string;
}
function ajax(config: Config) {
var xhr = new XMLHttpRequest
xhr.open(config.type, config.url, true)
xhr.send(config.data)
xhr.onreadystatechange = function() {
if(xhr.readyState == 4 && xhr.status == 200) {
if(config.dataType == 'json'){
console.log(JSON.parse(xhr.responseText))
}else{
console.log(xhr.responseText)
}
}
}
}
ajax({
type: 'get',
data: 'name=xiaoming',
url: 'http://a.itying.com/api/productlist',
dataType: 'json'
})
复制代码
1.2 函数类型接口
对方法传入的参数以及返回值进行约束
interface encrypt{
(key: string, value: string): string; // 传入的参数和返回值的类型
}
var md5:encrypt = function(key:string, value:string):string{
// encrypt对加密方法md5进行约束,同时md5方法的参数和返回值类型和encrypt要保持一致
return key + value
}
console.log(md5('name', '小明'))
复制代码
1.3 可索引接口
对索引和传入参数的约束(一般用于对数组、对象的约束)
ts中定义数组:
var arr1:number[] = [1,2]
var arr2:Array<string> = ['1', '2']
复制代码
现在用接口来实现:
// 对数组的的约束
interface UserArr{
// 索引为number,参数为string
[index:number]: string
}
var userarr:UserArr = ['a', 'b']
console.log(userarr)
复制代码
// 对象的约束
interface UserObj{
// 索引为string,参数为string
[index:string]: string
}
var userobj:UserObj = { name: '小明', sex: '男' }
console.log(userobj)
复制代码
1.4 类类型接口
对类的约束,和抽象类抽象有点相似
interface Animal{
// 对类里面的属性和方法进行约束
name:string;
eat(str:string):void;
}
// 类实现接口要用implements关键字,必须实现接口里面声明的方法和属性
class Cat implements Animal{
name:string;
constructor(name:string){
this.name = name
}
eat(food:string){
console.log(this.name + '吃' + food)
}
}
var cat = new Cat('小花')
cat.eat('老鼠')
复制代码
2. 接口的继承
和类的继承一样,用extends实现接口继承
下面同时实现类的继承和接口的继承
interface Animal {
eat(): void;
}
// 继承Animal接口,则实现Person接口的类必须也实现Animal接口里面的方法
interface Person extends Animal {
work(): void;
}
class Programmer {
public name: string;
constructor(name: string) {
this.name = name;
}
coding(code: string) {
console.log(this.name + code)
}
}
// 继承类并且实现接口
class Web extends Programmer implements Person {
constructor(name: string) {
super(name)
}
eat() {
console.log(this.name + '吃')
}
work() {
console.log(this.name + '工作');
}
}
var w = new Web('小李');
w.eat();
w.coding('写ts代码');
复制代码
六、TypesSript泛型
泛型定义:泛型定义:泛型就是解决类、接口、方法的复用性,以及对不特定数据类型的支持(类型校验)。ts中用T表示泛型。
泛型公式: 表示泛型,调用的时候指定T的数据类型
软件工程中,我们不仅要创建一致的定义良好的API,同时也要考虑可重用性。 组件不仅能够支持当前的数据类型,同时也能支持未来的数据类型,这在创建大型系统时为你提供了十分灵活的功能。
在像C#和Java这样的语言中,可以使用泛型来创建可重用的组件,一个组件可以支持多种类型的数据。 这样用户就可以以自己的数据类型来使用组件。
内容概述:内容概述:函数的泛型、类的泛型、泛型接口
1. 函数的泛型
传入的参数类型和返回的参数类型可以指定
我们来看看函数用ts数据类型,想要同时返回string类型和number类型
function getData1(value:string):string{
return value;
}
function getData2(value:number):number{
return value;
}
复制代码
这样要写不同的函数,不能按照需求返回不同类型数据,造成代码冗余 => 由此引入泛型
表示泛型,调用的时候指定T的数据类型
function dataT<T>(value:T):T{
// 传入参数为T 返回值为T
return value
}
dataT<number>(1) // 调用指定泛型为number类型,则传入参数也必须为number类型
dataT<string>('string')
function dataAny<T>(value:T):any{
return '传入参数为T,任意类型返回值';
}
dataAny<number>(123); // 参数必须是number
dataAny<string>('这是一个泛型');
复制代码
2. 类的泛型
也是用来实现类的泛型,new的时候指定T的数据类型
有个最小堆算法,需要同时支持返回数字和字符串两种类型
使用泛型之前:只能在类的类部指定数据类型,实现需求还要写一套string类型的类
class MinClass{
public list:number[]=[];
add(num:number){
this.list.push(num)
}
min():number{
var minNum=this.list[0];
for(var i=0;i<this.list.length;i++){
if(minNum>this.list[i]){
minNum=this.list[i];
}
}
return minNum;
}
}
var m=new MinClass();
m.add(1);
m.add(2);
alert(m.min());
复制代码
使用泛型之后:只用一套类来实现
class MinClassT<T>{
public list:T[]=[];
add(value:T):void{
this.list.push(value);
}
min():T{
var minNum=this.list[0];
for(var i=0;i<this.list.length;i++){
if(minNum>this.list[i]){
minNum=this.list[i];
}
}
return minNum;
}
}
var m1=new MinClassT<number>(); /*实例化类 并且指定了类的T代表的类型是number*/
m.add(1);
m.add(2);
alert(m1.min())
var m2=new MinClassT<string>(); /*实例化类 并且指定了类的T代表的类型是string*/
m2.add('c');
m2.add('a');
alert(m2.min())
复制代码
3. 泛型接口
有一个函数类型接口
interface ConfigFn{
(value:string):string;
}
var setData:ConfigFn = function(value:string):string{
return value
}
setData('name');
// setData(20); // 错误
复制代码
setData(20);写法错误,想要传入number类型的参数又要写一个函数类型接口 => 用泛型接口
泛型接口有两种写法:
// 泛型接口定义方式一
interface ConfigFnOne{
<T>(value:T):T;
}
var setDataOne:ConfigFnOne = function<T>(value:T):T{
return value
}
// 既可以传入string也可以传入number类型参数
setDataOne<string>('name');
setDataOne<number>(20);
复制代码
// 泛型接口定义方式二
interface ConfigFnTwo<T>{
(value:T):T;
}
function setDataTwo<T>(value:T):T{
return value
}
var setDataTwoFn:ConfigFnTwo<string> = setDataTwo
setDataTwoFn('name');
复制代码
七、TypesSript类型推论
有的时候不一定需要强制使用类型声明,在有些没有明确指出类型的地方,ts类型推论会帮助提供类型。
内容概述:变量初始化类型推论、上下文类型推论。
变量初始化
ts会根据变量初始化的时候赋予的值进行类型推断。
let a = '类型推论';
// a = true; // Type 'true' is not assignable to type 'string'
复制代码
上面代码中,a初始化没有指定类型,ts会推论出a的类型为string,当a = true重新赋值的时候类型不匹配会报相应错误,vscode编译器会提示错误。
上下文推断
ts也会根据上下文进行类型的推断,比如在事件函数中,函数的第一个参数会根据当前绑定的事件类型推断处理事件对象。
document.onkeydown = function(e) {
// console.log(e.button); //<- Error Property 'button' does not exist on type 'KeyboardEvent'
};
复制代码
这个例子会得到一个类型错误,ts类型检查器根据当前绑定的事件类onkeydown自动推导e的类型为KeyboardEvent,vscode编译器里鼠标放上去就有e推导出来的类型(e:KeyboardEvent)
所有评论(0)