TypeScript学习(上)
TypeScript的介绍1.ts是由微软开发的开源变成语言(vscode和ts都是微软提供的,vs里面很多ts的适配,里面有很多ts的插件,让写法更佳舒适)2.ts是js的超集(超集的理解:ts在js之上,包含js所有最新的语法特性,包含es6、es7所对应的写法)3.ts是开发大型应用的基石(为什么要学ts?很多大型项目都是基于ts来开发的,比如vscode,vue 3.0,react;越来越
TypeScript的介绍
1.ts是由微软开发的开源变成语言(vscode和ts都是微软提供的,vs里面很多ts的适配,里面有很多ts的插件,让写法更佳舒适)
2.ts是js的超集(超集的理解:ts在js之上,包含js所有最新的语法特性,包含es6、es7所对应的写法)
【 es 3一个版本,es5一个版本 ,es 6一个版本,es2017,2018,2019都是es7版本】
3.ts是开发大型应用的基石(为什么要学ts?很多大型项目都是基于ts来开发的,比如angular,vscode,vue 3.0,react;越来越多的特性都偏向ts,大型应用基本都用ts语法做支持)
4.ts提供了更丰富的语法提示
5.ts在编译阶段能检查语法错误
安装
1.先去官网安装node
2.npm i typescript
错误检查&ts的编译
编译:
(为什么需要编译?因为浏览器无法识别ts,所以需要将ts编译成js)
var a:string='123';
console.log(a);
执行tsc index.ts,将index.ts文件编译为index.js文件。
创建配置文件:tsc --init , 配置完rootDir和outDir后,直接输入tsc编译
错误检查:
但是这段代码在js中却可以正确运行
js是动态类型,可以动态更改变量的类型
var a = '123';
a=123;
console.log(a);
ts是静态类型的(一旦定义完类型后就无法更改它的类型):
ts中的数据类型&&ts新增的语法特性
数据类型
js:基本类型:boolean 、string、number、null、undefined、symbol
引用类型:object
ts:数据类型:
- number
let num: number = 6;
- string
let str: string = '123';
- boolean
let flag: boolean = false;
- any
let obj:{ // 对象类型的注解 str: string, toString: () => void } = { str: '124', toString: function () { } } console.log(obj); // 或这样写1 // interface Obj { // str: string; // toString: () => void // } // let obj: Obj = { // str: '123', // toString:function () { // } // } // 或这样写2 // type Obj = { // str: string; // toString: () => void // } // let obj: Obj = { // str: '123', // toString:function () { // } // }
let obj:any = { str: '124', toString: function () { } } console.log(obj);
表示任意数据类型。
1)如果是对象的话,any不能提示原有的属性和方法
2)如果是不同变量的话,可以是任意的数据类型
3)未给初始值的变量类型为any (比如let a; 这里的a就是any类型,所以可以a='123'; a=123;不会报错)
- undefined ,
配置项中--strictNullChecks会严格检查null和undefined类型,赋值会赋不成功 (想赋的话把此参数改成true,但是官网不建议这么赋值,想这样赋的话就用联合项) 推荐写法:let num: number = undefined;
let num: number | undefined = undefined;
- null ,null和undefined是所有类型的子类型。
- never,表示永不存在值的类型。当报错或死循环的时候用它。
function error(message) { throw new Error(message); } function fail() { return error('something failed'); } function infiniteLoop() { while (true) { } }
- void :表示没有类型,用在函数没有返回值的时候。
function test(): void { console.log(1111); }
- 枚举
- 数组 :
// 数组的注解;有3种表示方法 // 1.类型[] let arr1: number[] = [1, 2, 3]; // 2.Array<类型> Array<number> let arr2: Array<number> = [1, 2, 3]; // 3.interface interface NumberArray { [index: number]: number; } let list: NumberArray = [1, 2, 3, 4]; // 类数组 function test() { let args: IArguments = arguments; }
- 元祖
Object:非原始类型。
// 全局声明一个函数,规定这个函数的参数是对象或null,没有返回值
declare function create(o: object | null): void;
create({ prop: 0 }); // OK
create(null); // OK
create([]); // OK
create(function(){}); // OK
create({}); // OK
// 由此看出,这里的object就是非原始类型
进一步的区分:interface、number[]、string[]、boolean[]
数组中泛型的写法:Array<number>、Array<string>、Array<boolean>
函数注解:
let test=function(a:number,b:number):number{
return a+b;
}
类型注解
开发者自己定义的ts变量类型就是类型注解
类型推断
当我们没有指定类型的时候,ts会自动帮我们分析当前的变量类型。
(当类型推断能推断出正确的类型时,就不用类型注解,比如函数这种情况下
function add(a, b) {
return a + b;
}
add(1, 2);
)
联合类型
注意:
1.联合类型的公有属性是不会报错的;比如toString(),number和string类型都有这个方法就不会报错。
2.在赋值的时候就已经确定了数据的类型。不能更改类型了。
接口
对对象的形状进行描述;
对类的一部分的行为的抽象;
interface Person {
name: string;
age: number;
}
let person: Person = {
name: "lily",
age: 18,
};
【interface定死了以后,它的属性不可多,也不可少
】
如果想要多或少属性怎么办呢?
可少;可以用可选属性?:
可多;用任意属性[propName:string]:any
如果遇到id这种唯一不可变的属性,可以用readonly属性
改造:
ReadonlyArray<T>
类型
let ro: ReadonlyArray<number> = [1,2,3,4];
readonly
还是 const?
当做变量用const,当做属性用readonly(主要判断属性是否可写)
函数类型接口
(之前是按这样的方式来注解函数,有1个问题:在注解函数的同时实现了这个函数,如果我只想要定义这个函数类型,没办法定义)
function mySearch1(source: string, subString: string): boolean {
let result = source.search(subString);
return result > -1;
}
函数表达式的方式太冗长
let mySearch: (source: string, subString: string) => boolean = function (
source: string,
subString: string
): boolean {
let result = source.search(subString);
return result > -1;
};
我们希望抽象出来一个参数是source和subString,返回值是boolean的函数类型
第一种(不常见):
type SearchFunc = (source: string, subString: string) => boolean;
let mySearch: SearchFunc = (source: string, subString: string): boolean => {
let result = source.search(subString);
return result > -1;
};
第二种(常见):
interface SearchFunc {
(source: string, subString: string): boolean;
}
let mySearch: SearchFunc = (source: string, subString: string): boolean => {
let result = source.search(subString);
return result > -1;
};
可索引类型接口(就是数组类型接口)
当索引是number时候,就可以描述一个数组:
interface numberArr {
[index: number]: number;
}
let test: numberArr = {
0: 1,
1: 2,
};
let arr: numberArr = [1, 2, 3];
注意:索引签名支持2种形式:字符串和数字。因为所有的number最终会转为字符串,当使用 number
来索引时,JavaScript会将它转换成string
然后再去索引对象。
官网例子:
(1)为什么第一个索引签名是number会报错? 因为Animal父类的name是string类型
(2)那如果把第一个改成string又为什么会报错?因为两者重复。
(3)如果把第二个索引签名改成number类型为什么不报错?因为所有number最后都被会js转成string类型。
(4)如果number都会被转为string,那么回到第一个问题,为什么不能给第一个索引签名设置为number?因为string类型范围比number范围大,Animal是父类, 父类必须比子类范围要大,所以第一个必须是string,第二个必须是number
索引签名有点类似老大的意思,索引签名是个大范围,接口里面的属性必须得在这个范围内。
interface NumberDictionary {
[index: string]: number | string;
length: number;
name: string;
}
索引签名也可以设置为只读:
类 类型接口
可以用接口来描述类吗?不完全可以,因为类有构造器。
“对类的一部分的行为的抽象”的理解:
把类上 抽象的公共属性和方法,抽成一个接口。
想要解决这个报错问题,可以改配置项"noImplicitAny": false
interface Alarm {
alert(): void;
}
interface Light {
color: string;
lightOn(): void;
lightOff(): void;
}
class Door {}
class SecurityDoor extends Door implements Alarm {
alert() {
console.log("hi");
}
}
class Car implements Alarm, Light {
color = "red";
lightOn() {}
lightOff() {}
alert() {}
}
类和接口对比:
(1)抽象类中定义抽象方法要实现;
接口中定义的属性方法都要实现;
(2)抽象类中的方法属性可以通过继承方式来拿到;
接口的抽象方法需要实现;实现接口的话,接口所有方法都要实现;
(3)1个类可以实现多个接口;多个子类可以继承1个抽象的类
用类 分别实现类的静态部分与实例部分
(类的静态部分和实例部分需要单独写)
想通过接口直接实现类的构造函数是会报错的,因为constructor存在于类的静态部分,所以不在检查的范围内。
但是也有办法:
单独抽离1个构造函数的interface,用函数检查的方式单独检查函数的interface,然后返回一个new的实例
interface ClockInterface {
currentTime: Date;
getTime(h: number, m: number): void;
}
interface ClockConstructor {
new (h: number, m: number): any;
getTime1():void; // 相当于一个静态方法
}
class Clock implements ClockInterface {
currentTime = new Date();
getTime() {}
constructor(h: number, m: number) {}
static getTime1() { }
}
function createClock(Clock: ClockConstructor, h: number, m: number) {
return new Clock(h, m);
}
let clock = createClock(Clock, 12, 12);
继承接口
就是接口可以继承接口 或 类可以实现接口。
“接口可以继承接口”的理解:
接口是对象形状的描述,接口继承接口就相当于在原本基础上进行进一步的约束。
interface Shape {
color: string;
}
interface Square extends Shape {
sideLength: number;
}
let square = <Square>{}; // 这里接口继承接口,然后用断言的方式赋初始值
square.color = "blue";
square.sideLength = 10;
官方这里没有解释为什么这里要用类型断言:
如果let square:Square;这样写,报错提示:“在赋值前就使用了变量”;定义了square,但是没有初始值,但是一旦给初始值,就回去检查它类型,就会类型不符,所以用类型推断方式。
接口的混合类型
函数类型的interface,可以通过添加属性的方式来实现对象的interface
相当于函数类型interface+对象类型interface
interface Counter {
(start: number): string;
interval: number;
reset(): void;
}
function getCounter(): Counter {
let counter = <Counter>function (start: number) { };
counter.interval = 123;
counter.reset = function () { };
return counter;
}
let c = getCounter();
c(10);
c.reset();
c.interval = 5.0;
接口继承类
1.类可以实现接口:类可以抽象一部分功能,让接口实现
2.接口可以继承接口:接口相当于一个对象,继承一个对象,就是能在这个对象上面添加更对属性
3.接口可以继承类:类也是接口,接口可以继承接口,所以接口能继承类。
报错:
1.image重复,改成image1。
2.提示错误实现接口SelectableControl,因为接口继承Control接口:在Image里定义state,但是因为是私有成员,所以没法直接实现state。
接口和类的关系:
// 2种写法效果是一样的
class Point {
x: number;
y: number;
constructor(x: number, y: number) {
this.x = x;
this.y = y;
}
}
interface PointerInterface {
x: number;
y: number;
}
interface Point3D extends Point {
z: number;
}
interface Point3D extends PointerInterface {
z: number;
}
虽然这种写法方式没有问题,但容易引起大家歧义,不建议大家用接口继承类。
函数
函数有2种写法:函数声明和函数表达式。
函数声明的注解方式:
function test(a: number, b: number): number {
return a + b;
}
函数表达式的注解方式:
let test1: (a: number, b: number) => number = (a, b) => {
return a + b;
};
let test2: (a: number, b: number) => {} = (a, b) => {
return { a, b };
};
console.log(test2(1, 2));
当参数少了的时候同interface可以设置可选参数
【可选参数注意事项】
(1)默认把可选参数放在后面,必选参数不能位于可选参数后。
function test(a: string, b?: string): string {
return "" + a + b;
}
console.log(test("1", "2"));
(2)默认值和可选参数不能放一起。
当参数多了可以用剩余参数 ,...args,...args是个数组,必须放到最后面
// 语法
function test1(
{ first, second }: { first: number; second: number } = { first: 1, second: 2 }
) {
return first + second;
}
function test2({ first = 2 }: { first: number }) {
return first;
}
// 解构赋值
// 变量和属性名一致时,对象可简写
// let {a:a,b:b,c:c}={a:1,b:2,c:3};
// 等价于
// let {a,b,c}={a:1,b:2,c:3};
// 不完全结构是值(右边)多了;结构失败是变量(左边)多了
关于this
this有4个规则:
(1)默认情况下this指向window。(函数的独立调用、立即执行函数、闭包中的this都指向window)
(2)隐式绑定,谁调用就指向谁。
(3)显示绑定,call、apply、bind绑定this指向
(4)new的this指向实例化后的对象
官网例子
报错的this指向window,我们希望这里的this指向deck。
就可以这么写
noImplicitThis配置项
控制当源文件中存在this的值是any的时候是否报错,noImplicitThis默认为false,即当源文件中存在this为any的情况也不报错,如果设置为true则会报错
函数的重载
ts的重载解决的只是表意问题,能够表意更清楚
function reverse(x: string): string;
function reverse(x: number): number;
function reverse(x: number | string) {
if (typeof x === "string") {
return x.split("").reverse().join("");
} else {
return Number(x.toString().split("").reverse().join(""));
}
}
console.log(reverse(123));
编译后
类
类的注解方式
class Greeter {
greeting: string;
constructor(message: string) {
this.greeting = message;
}
greet() {
return "Hello, " + this.greeting;
}
}
let greeter = new Greeter("world");
继承
继承中的super在constructor内部默认是构造函数;在这以外super指的是父类。
class Animal {
name: string;
constructor(theName: string) {
this.name = theName;
}
move(distanceInMeters: number = 0) {
console.log(`${this.name} moved ${distanceInMeters}m.`);
}
}
class Snake extends Animal {
constructor(name: string) {
super(name);
}
move(distanceInMeters = 5) {
console.log("Slithering...");
super.move(distanceInMeters);
}
}
let sam = new Snake("Sammy the Python");
sam.move();
类成员的修饰符
public:
(1)自身调用:
(2)子类调用:
(3)实例调用:
private:
(1)只能自身调用
protected:(控制可访问否,上面都是)
(1)自身可以调用(2)子类可以调用
readonly:(控制可写否)
顺序:public readonly
不能修饰成员方法
参数属性
参数属性可以方便地让我们在一个地方定义并初始化一个成员。
class Octopus1 { // 简写
constructor(private theName: string) {}
}
class Octopus2 {
private theName: string;
constructor(theName: string) {
this.theName = theName;
}
}
存取器(改变赋值和读取的行为)
1.成对出现,定义了getter一定也有setter,并且修饰同一属性
2.这里的赋值操作相当于调用了set方法,判断里用到了fullName属性,相当于调用了get方法。
静态属性
静态属性用static来定义,通过类/构造函数来访问。如果是实例属性用this.来访问的。
抽象类
abstract关键字来定义抽象类,抽象类能够作为其它派生类的基类使用。
特点:(1)无法创建实例
(2)抽象类中的抽象方法一定要实现
abstract class Animal {
abstract makeSound(): void; // 发声方式
move(): void {
console.log("roaming the earch...");
}
}
class Snack extends Animal {
makeSound() {
console.log("zzzzzzzz");
}
move(): void {
console.log("roaming the earch...");
}
}
class Cow extends Animal {
makeSound() {
console.log("mmmmmmmmmm");
}
move(): void {
console.log("roaming the earch...");
}
}
高阶技巧
1.定义了一个类的时候,相当于定义了一个类型/构造函数
2.接口可以继承
更多推荐
所有评论(0)