typescript介绍:
- ts是由微软开发的开源的编程语言
- ts是js的超集
- ts是开发大型应用的基石
- ts提供了更加丰富的语法提示
- ts在编写阶段就可以检查错误
- ts 是静态类型,js 是动态类型
- ts 需要被编译成 js 文件后,才可以直接在浏览器和node中运行的 (使用
tsc命令)- 所有的ts文件最后都需要被编译成js文件才能执行
1 + undefined = NaN- 在
ts中类型是确定的,给变量规定类型之后,其类型不能被改变
typescript中的数据类型
- 在js中,有原始数据类型和引用数据类型:
- 原始数据类型:boolean string number null undefined symbol
- 引用数据类型:object
- ts中的数据类型:
- 基础类型: boolean、string、number、null、undefined、symbol、any(新增) never(新增)、void
- 引用类型被做了细分:
- 对象: interface(可以描述一个对象的结构)
- 数组: number[], string[], boolean[], 泛型的写法:Array
- 函数: 可以规定形参的类型以及返回值的类型
let test = function(a: number, b: number): number {
return a + b;
}
- 新的语法特性:
- as 断言
- class,基于原本的class,多了面向对象的三大特性:封装、继承和多态;
布尔、数值、字符串、void的注解(原始数据类型的注解)
- 布尔值的注解:
let isDone: boolean = false;
- 数字的注解:
let num: number = 6let num1: number = 0o744(可以直接赋值在js中报错的八进制数)
- 字符串的注解:
let str: string = 'Chase'
- any 的注解:
- 如果是普通变量的话,可以是任意的数据类型
- 如果是对象的话,
any是不能提示原有的属性和方法
let notSure: any = 4;
notSure = "maybe a string instead";
notSure = false;
- 未给初始值的变量类型为
any
Void类型:当一个函数没有返回值时
function test(): void{
console.log('');
}
- null & undefined的注解:
null和undefined是所有类型的子类型(需要修改配置:strictNullChecks,默认为true,需要改为false)- 修改了如上的配置项之后,就可以把null以及undefined赋值给任意类型的变量(官方不推荐)
let num: number = undefinedlet bol: boolean = null
- 官方更推荐使用联合类型:
let num: number | undefined | null = undefined;let bol: boolean | undefined | null = null;
- never的注解:
- 表示那些永远不存在的值
function error(message: string): never{
throw new Error(message);
}
function fail(){
return error("Something failed")
}
function infiniteLoop(): never {
while(true){}
}
- object的注解:
- 表示非原始类型,可以表示对象、数组、函数
declare用于规定函数的形参类型以及返回值类型,并不关心该函数如何实现,所以后面不需要跟上函数的实现细节- 对于object的注解用于更好的实现如
Object.create的API
declare function create(o: object | null): void;
create({prop:0}); //OK
create(null); //OK
create({}) //OK
create(42); //Error
create('string') //Error
- 在配置ts编译目录的时候,可以修改
tsconfig下的outDir和rootDir- 步骤:
$ tsc --init- 在生成的
tsconfig.json文件下找到outDir以及rootDir outDir: ./distrootDir: ./src- 直接终端输入
tsc, 就可以自动查找需要编译的源目录以及将生成的文件传入到dist目录
- 步骤:
类型注解和类型推断
- 类型注解:
let count: number;
count = 993;
- 类型推断(如果ts可以帮忙做类型推断,则尽量不使用类型注解,减少代码量)
let count = 3; //ts直接推断count的类型为number
//ts不能推断类型的情况
function test(a, b){ return a+b;}
test(1,2); //会报错
- 联合类型
- 定义:表示一个变量可以为多个类型的时候,就可以使用联合类型
let a: string | number;
a = '123';
a = 123;
- 注意:
- 联合类型的共有属性不会报错
- 在赋值的时候,变量的类型被确定
let a: string | number;
a = '123';
console.log(a.length); //=> 3
a = 10;
console.log(a.length); //=> error
数组的注解
- 使用
number[]来注解:
let list: number[] = [1,2,3];没有长度限制, 只能存储数字- 让数组既可以存数字又可以存字符:
let list: (number|string)[] = [1,'2',3,'4']
- 使用泛型注解:
let list: Array<number> = [1,2,3]
- 使用interface来表示数组
interface List {
[index: number]: number | string; //可以添加无限数量的元素,数组长度不受限
}
let list: List = [1,2,3,4,5,'6']
类数组情况:
interface Args {
[index: number]: any; //键和值可以为任意属性
length: number;
callee: Function;
}
function test(){
let args: Args = arguments;
}
函数的注解
包括:1. 函数声明的注解方式; 2. 函数表达式的注解方式
//函数声明
function test(a, b){
return a + b;
}
//函数表达式
let test1 = function(a, b){
return a + b;
}
//函数声明的注解
function test(a: number, b: number){
return a + b;
}
//函数声明的注解
function test(a: number, b: number): number{
return 1;
//console.log(1) //函数返回值为void
//throw new Error() //函数返回值为never
}
//函数表达式的注解,箭头后面的number代表的是返回值的类型而不是箭头函数的执行体
let test1: (a: number, b: number) => number = function(a,b){
return a + b;
}
let test2: (a: number, b: number) => {} = function(a,b){//返回值为对象
{ a: 1 };
}
可选参数和默认参数
- 可选参数:
function buildName(firstName: string, lastName?: string){
//lastName可以为null, undefined
return firstName + " " + lastName;
}
- 默认参数
function buildName(firstName: string, lastName: string = 'Chase'){}
//可以简写,赋值字符串后会进行自动的类型推断
functionm buildName(firstName: string, lastName = 'Chase'){}
- 剩余参数(类似于ES6中的rest)
function t(fName: string, lName = 'Chase', ...restPara: string[]){}
- 解构赋值
function test(
{first, second}: {first:number, second: number} = {fist:1, second: 2}){
return first + second
}
function test({first: first = 2}:{first:number}){
//键名和键值相同就简写:function test({first:2}: {first: number})
}
this指向相关
- 箭头函数能保存函数创建时的
this值,而不是调用时的值 - 可以通过箭头函数来绑定
this指向 - 在ts配置项中
noImplicitThis -> false,可以设置this是否为严格模式,严格undefined,非严格指向window - 官网例子:
interface Card {
suit: string;
card: number;
}
interface Deck {
suits: string[];
cards: number[];
createCardPicker(this: Deck): () => Card;
//类型是(),也就是函数,返回值是Card类型的变量
}
let deck: Deck = {
suits: ["hearts", "spades", "clubs", "diamonds"],
cards: Array(52);
createCardPicker: function (this: Deck){//这里指明this类型
return ()=>{//这里把function改成箭头函数就可以正常保存之前的this值
let pickedCard = Math.floor(Math.random()*52);
let pickedSuit = Math.floor(pickedCard / 13);
return {
suit: this.suits[pickedSuit],
card: pickedCard
}
}
}
}
let cardPicker = deck.createCardPicker();
let pickedCard = cardPicker();
ts中函数的重载
单纯为了使得相关的表达更加清楚。(表义更加清楚)
function reverse(x: string): string;
function reverse(x: number): number;
function reverse(x: string | number) {
if(typeof x === 'string'){
return x.split('').reverse.join('');
}
if(typeof x === 'number'){
return Number(x.toString().split('').reverse().join(''))
}
}
类
- 类中的所有属性都需要注解
class Greeter {
greeting: string;
constructor(message: string){
this.greeting = message;
}
greet(){
return 'Hello, ' + this.greeting;
}
}
let greeter = new Greeter('world');
继承
//父类,也叫基类
class Animal {
move(distanceInMeters: number = 0){//number初始值为0
console.log(`Animal moved ${distanceInMeters}m.`)
}
}
//子类,也叫父类
class Dog extends Animal {
bark() {
console.log('Woof! Woof!');
}
}
const dog = new Dog();
dog.bark();
dog.move(10);
- 注意:
tsconfig文件中的strictPropertyInitialization用来要求变量必须初始化- 类的注解方式就是给类中所有的变量注解类型
类的修饰符:
面向对象(OOP)的三大特性: 封装,继承,多态
- 封装:
Private,Public,Protected是类成员的修饰符- public: 公有成员
- 自身可以直接调用
- 子类可以直接调用
- 实例可以直接调用
- public: 公有成员
class Animal {
public name: string;
public constructor(theName: string){this.name = theName;}
public move(distanceInMeters: number){
console.log(`${this.name} moved ${distanceInMeters}m.`);
}
}
- private: 私有成员变量
- 自身可以直接调用
- 子类不可以直接调用
- 实例不可以直接调用
- protected: 受保护的成员变量
- 自身可以调用
- 子类可以调用
- 实例不可以调用
readOnly修饰符- 规定属性是只读的,不可被修改,只能在声明或构造函数里使用,跟在public后面
- 不能修饰成员方法
class Animal {
public readonly name: string;
}
- 参数属性
constructor(private name: string){}//常用这种写法
//等价于下面的代码
private name: string;
constructor(name: string){this.name = name}
存取器
- 本质就是给一个类实现一个
get和set,get是取值函数,set是存值函数 - 初始化存取器之后,给私有成员变量赋值就可以直接
instance.variable = 123;类会隐式地自动调用相关的get和set函数,而不需要显式的调用instance.getName()或instance.setName() get和set应该成对出现,不应该只定义其中一个
class Employee{
private _fullName: string = 'initial Name';
get fullName(): string{
return this._fullName;
}
set fullName(newName: string){
this._fullName = newName;
}
//两个函数名是一样的,不一样的是前面的set和get
}
静态属性和抽象类
- 静态属性: static,可以直接
class.staticVariable进行调用 - 抽象类:
- 能够提供其他类的基类
- 父类身上有可以复用的方法的时候,可以使用抽象类
- 注意1: 无法创建实例,无法使用
new关键字 - 注意2: 抽象方法一定要被实现
- 注意3: 抽象方法必须在派生类里实现,而不是直接在抽象类里实现
abstract class Animal {
abstract makeSound(): void;
move(): void {console.log('moving...')}
}
new Animal(); //抽象类不能使用new关键字,会报错
class Snake extends Animal{
makeSound(){
//必须要实现这个方法
console.log('ssssssssss');
}
}
TS高级技巧
- 在定义一个类的时候,也就相当于定义了一个数据类型,在进行数据注解的时候,可以将相关变量的类型注解设置为这个类
- 定义一个类的时候,也就相当于定义了一个构造函数
class Greeter{}
let greeter1: Greeter = new Greeter();
let greeterMaker: typeof Greeter = Greeter; //类型注解为构造函数
//greeterMaker相当于一个构造函数
let greeter2: Greeter = new greeterMaker();
- 接口可以继承类
class Point{
x: number;
y: number;
}
interface Point3d extends Point{
z: number;
}
let point3d: Point3d = {x:1, y:2, z:3}
- 补充
1: 可以通过设置
tsconfig里的target属性来决定编译出来的js版本(es5,es6等) 2: 在注解类的时候,只需要注解其中变量的的数据类型即可
接口 interface
- 定义:对“对象”的形状进行描述
//原来给object加注解:
let person: {
num: String,
toString: ()=>{}
} = {
num: '123',
toString: function(){}
}
//使用interface
//首先定义一个interface
interface Person {
name: string,
age: number
}
//然后用刚刚定义好的interface来给对象加注解
let person1: Person = {
name:'chase',
age:23
}
- 注意
- 正常情况下,多添加属性或少添加属性都会报错
- 如果不确定是否一定需要某个属性,可以在该属性后加一个问号(?)
interface Person{
name: string;
age?: number; //这样在后面初始化Person实例时,没有初始化age也不会报错
}
- 如果需要添加某一属性:
interface Person {
name: string;
age?: number;
[propName: string]: any;
//使用这行代码可以在初始化Person时添加任意数量的新属性
//任意属性的类型的值一定是any
}
- 如果想要某个属性为只读属性,不允许被外部修改,可以使用
readOnly关键字
interface Person {
name: string;
readOnly id: number;
}
对类的一部分行为的抽象
- 接口初探
interface LabelledValue {
label: string;
}
function printLabel(labelledObj: LabelledValue){
console.log(labelledObj.label);
}
let myObj = {size:10, label: "Size 10 Object"}
printLabel(myObj);
- 使用接口对函数进行注解
//先定义一个接口
interface SearchFunc {
(source: string, subString: string): boolean;
//形参类型 返回值类型
}
//后面的函数注解就可以直接使用先前定义好的接口
let mySearch: SearchFunc;
mySearch = function(source: string, subString: string){
let result = source.search(subString);
return result > -1;
}
还可以直接使用type关键字来注解函数:
type SearchFunc = (source: string, subString: string) => boolean;
let mySearch: SearchFunc = function (source: string, subString: string): boolean{
let result = source.search(subString);
return result > -1;
}
接口和可索引的类型接口
interface NumberArray {
//左边代表索引,索引的类型为number时,可以描述一个数组
//右边代表索引对应的值
[index: number]: number
}
let obj: NumberArray = {
0: 1,
1: 2,
2: 3
}
let arr: NumberArray = [1, 2, 3, 4, 5]
- 注意1:当使用number来索引时,JavaScript会将它转换为string然后再去索引对象
class Animal {name: string}
class Dog extends Animal {breed: string}
interface animalInterface {
[x: string]: Animal;
[x: number]: Dog;
//number会继承string???
}
- 注意2: 在interface中索引签名的优先级最高,可以给索引签名设置readonly,设置readonly的属性在之后初始化出来的实例都是不能修改的
interface NumberDictionary {
[index: string]: number; //索引签名,优先级最高
length: number;
name: string; //报错,与索引签名要求的不一致
}
interface Person {
name: string;
age: number; //报错,理由同上,
//修改 [propName: string]: string | number就不会报错了
[propName: string]: string;
}
类类型接口: 对类的一部分行为的抽象
interface ClockInterface {
currentTime: Date; //接口中只是定义了属性的类型
setTime(d: Date): void; //描述一个方法,参数在左边,返回值在右边
}
//类需要能实现接口
class Clock implements ClockInterface {
currentTime = new Date(); //类中需要定义接口中属性的实现方式
setTime(){
//需要实现接口中声明的方法
}
}
拓展例子,定义一个接口用来报警,定义一个Door类,然后定义Door类的派生类SecurityDoor
interface Alarm {
alert(): void;
}
interface Light{
color: string;
lightOn(): void;
}
class Door {}
class SecurityDoors extends Door implements Alarm {
alert(){
console.log('ViWu~ ViWu~')
}
}
class Car implements Alarm, Light{//可以实现多个接口
alert(){
console.log('didiididididididi')
}
color = 'red';
lightOn(){
console.log('lightOn')
}
}