TypeScript是JavaScript的超集,主要提供类型系统和对ES6的支持
使用TypeScript好处
- 增加代码可读性和我可维护性,在编译阶段发现问题,而不是运行时
- typescript具有包容性,js文件可以直接更名为ts文件,编译报错也能生成js文件
- 社区活跃,兼容第三方库,支持ES6规范
安装
npm install -g typescript
编译
tsc hello.ts
初始化Ts项目
tsc --init
基础
原始数据类型
布尔值
let isDone:boolean = false;
字符串
let str:string = 'hello';
数字
let num:number = 123;
空值
可以用 void 表示没有任何返回值的函数
function alertName(): void {
alert('My name is Tom');
}
null和undefined
let u: undefined = undefined;
let n: null = null;
任意值
any类型的变量,允许被赋值为任意类型
let myFavoriteNumber: any = 'seven';
myFavoriteNumber = 7;
任意值上允许访问任何属性,调用任何方法
let anyThing: any = 'hello';
console.log(anyThing.myName);
anyThing.setName('Jerry');
声名变量未指定其类型,它会被识别为任意值类型
let something 等价于 let something:any;
类型推断
定义变量未指定类型,会根据赋值推断出一个类型
let myFavoriteNumber = 'seven';
myFavoriteNumber = 7;
//报错
但是如果声明类型未赋值,会被推断成any任意类型
联合类型
联合类型表示可以是多种类型中的一种,使用 | 分隔,
只能访问此联合类型的所有类型里共有的属性或方法
let myFavoriteNumber: string | number;
myFavoriteNumber = 'seven';
myFavoriteNumber = 7;
数组类型
- 类型+[]表示法
let arr:number[]=[1,2,3,4];
- 数组泛型表示法
let arr:Array<number>=[1,2,3,4];
- 元组,数组每一项固定
let arr:[string,number] = [1,2];
- 接口表示
interface myArr {
[index:number]:number
}
函数类型
- 函数声明
function sum(x: number, y: number): number {
return x + y;
}
- 函数表达式
在 TypeScript 的类型定义中,=> 用来表示函数的定义,左边是输入类型,需要用括号括起来,右边是输出类型。
let mySum: (x: number, y: number) => number = function (x: number, y: number): number {
return x + y;
};
- 接口定义函数
interface myFun{
(param1:string,param2:string):boolean
}
let fun:myFun;
fun = function(param1:string,param2:string){
return param1 === param2
}
- 解构语法类型注解
function fn1({ first, last }: { first: number, last: number }): number {
return first + last;
}
- 可选参数
可选参数必须在必须参数后面
function buildName(firstName: string, lastName?: string) {
if (lastName) {
return firstName + ' ' + lastName;
} else {
return firstName;
}
}
- 默认参数
TypeScript 会将添加了默认值的参数识别为可选参数,不受「可选参数必须接在必需参数后面」的限制了
function buildName(firstName: string, lastName: string = 'Cat') {
return firstName + ' ' + lastName;
}
- 剩余参数
function push(array: any[], ...items: any[]) {
items.forEach(function(item) {
array.push(item);
});
}
- 重载
TypeScript 会优先从最前面的函数定义开始匹配,所以多个函数定义如果有包含关系,需要优先把精确的定义写在前面。
function reverse(x: number): number;
function reverse(x: string): string;
类型别名type
类型别名用于给类型起一个新的名字,比如:
type myStr = string
let str:myStr = 'fsdfds'
类型别名多用于联合类型
type Name = string;
type NameResolver = () => string;
type NameOrResolver = Name | NameResolver;
function getName(n: NameOrResolver): Name {
if (typeof n === 'string') {
return n;
} else {
return n();
}
}
字符串字面量类型, 限定只能是几个字符串中的一个
type EventNames = 'click' | 'scroll' | 'mousemove';
枚举类型
用于取值被限定在某一个范围内的场景
- 如果枚举未赋值,默认值从0开始,手动给某一项赋值,前面还是从0开始,后面递增1
enum Status{
offLine,
onLine
}
console.log(Status.offLine);//0
console.log(Status.offLine);//1
- 枚举可以部分赋值,未赋值的部分在上一个赋值的基础上+1
enum Status{
offLine=1,
onLine
}
console.log(Status.offLine);//1
console.log(Status.offLine);//2
- 枚举可以是小数或者负数,未赋值的部分在上一个赋值的基础上+1
enum Status{
offLine=1.5,
onLine
}
console.log(Status.offLine);//1.5
console.log(Status.offLine);//2.5
类与继承
ES6类的基本使用
- 通过class关键字声明类,类的名字大写
constructor是构造方法,内部的this指向实例对象,里面声明的属性是实例属性- 类上声明的函数,不需要
funtion,也不需要逗号分隔,是类原型上的方法 - 通过new关键字实例化一个对象
class Person {
constructor(name) {
this.name = name;
}
play() {
console.log("play");
}
}
let person = new Person("lxr");
上面这段代码,用ES5实现
function Person(name){
this.name = name
}
Person.prototype.play = function (){
console.log('play')
}
可以看出,ES6 class其实就是ES5 构造函数的语法糖,但是二者还是有一些不同的地方:
- class类只能通过
new调用,不能作为普通函数 - class类没有声明提前,只能先声明,再使用
- class类上声明的函数,都是不可枚举的
constructor
constructor是实例构造器,作为类的默认方法,如果不写会默认添加,在new的时候自动执行
class Point {
}
// 等同于
class Point {
constructor() {}
}
constructor默认返回this,指向实例对象,如果指定返回其他对象,那new出来的实例就不是类的实例
class Point {
constructor() {
return {name:'lxr'}
}
}
let point = new Point()//{name:lxr}
类的实例
- constructor中定义在this上的属性,是实例上的属性,除此之外都在原型上
class Person {
constructor(name) {
this.name = name;
}
play() {
console.log("play");
}
}
let person = new Person("lxr");
person.hasOwnProperty('name');//true
person.hasOwnProperty('play');//false
person.__proto__.hasOwnProperty('play') // true
- 类的实例跟es5的实例一样,共享一个原型对象
class Person {
constructor(name) {
this.name = name;
}
play() {
console.log("play");
}
}
let person1 = new Person("lxr");
let person2 = new Person("zpd");
console.log(person1.__proto__ === person2.__proto__);//true
取值函数(getter)和存值函数(setter)
在“类”的内部可以使用get和set关键字,对某个属性设置存值函数和取值函数,拦截该属性的存取行为。
class Person {
constructor(firstName, lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
get fullName() {
console.log("get");
return this.firstName + this.lastName;
}
set fullName(val) {
console.log("set", val);
}
}
let person = new Person("l", "xr");
console.log(person.fullName); //get lxr
person.fullName = "zpd";//set zpd
注意点
- 不存在声明提前
- 默认严格模式
- name属性总是返回紧跟在class关键字后面的类名
- 类的方法内部如果含有this,它默认指向类的实例。但是,必须非常小心,一旦单独使用该方法,很可能报错。
class Person {
constructor(name) {
this.name = name;
}
play() {
console.log(this.name);
}
}
let person = new Person("lxr");
const { play } = person;
play();
上面的代码会报错,因为play方法独立调用,class采用严格模式,所以 this 实际指向的是undefined,解决方案如下:
//提前绑定this
constructor(){
this.play = this.play.bind(this);
}
静态方法
类相当于实例的原型,类中的方法都会被实例继承,在方法前面加上static关键字,那么这个方法就不会被实例继承,通过类自身直接调用,称作静态方法,
class Person {
static walk() {
console.log("walk");
}
}
Person.walk();// Person walk
如果静态方法中存在this,那么this指向类,而不是实例
class Person {
static walk() {
console.log(this)
}
}
Person.walk();// Person
静态方法可以被子类继承
class Foo {
static classMethod() {
return 'hello';
}
}
class Bar extends Foo {
}
Bar.classMethod() // 'hello'
子类可以通过super调用父类静态方法
class Person {
constructor(name) {
this.name = name;
}
static walk() {
console.log(this);
}
}
class Sub extends Person {
static walk() {
super.walk();
}
}
Sub.walk();
class继承
class通过extends字段实现继承
class Person {
constructor(name) {
this.name = name;
}
}
class Sub extends Person {
}
上面代码中的子类继承了父类,什么也没做,所以相当于复制了一个父类
子类的构造函数必须执行一次super函数
class Person {
constructor(name) {
this.name = name;
}
}
class Sub extends Person {
constructor(name, age) {
super(name);
this.age = age;
}
}
let sub = new Sub("lxr", 18);
TS中的类
ES6中,类的实例属性只能在构造器通过this.XXX= XXX声明,Ts中,类的实例属性可以直接在类里面定义
class Animal {
name = 'Jack';
constructor() {
// ...
}
}
let a = new Animal();
console.log(a.name); // Jack
ES6中,只能定义静态方法,在ts中可以定义静态属性
class Animal {
static num = 42;
constructor() {
// ...
}
}
console.log(Animal.num); // 42
public、 private 和 protected修饰符
public定义的方法和属性都是共有的,所有属性和方法默认都是public
class Person{
public name;
public constructor(name){
this.name = name
}
}
let person = new Person('lxr')
console.log(person.name)//lxr
private定义的属性和方法都是私有的,不能在类的外面访问,也不能被子类访问, 构造器使用private,该类不能被继承和实例化-
protected定义的属性和方法,不能在类的外面访问,但是能在子类中访问,构造器使用protected,该类只能被继承,不能被实例化
修饰符可以在参数中使用
class Animal {
public name: string;
public constructor(name) {
this.name = name;
}
}
等价于
class Animal {
public constructor(public name) {
}
}
接口
接口可以用于对「对象的形状(Shape)」进行描述,接口的另一个用途,对类的一部分行为进行抽象
定义对象类型
- 用接口声明对象,形状必须保持一致
interface Person{
name: string;
age: number;
}
let person: Person = {
name: 'tom',
age:18,
}
?表示可选属性
interface Person{
name: string;
age: number;
height?:number
}
let person: Person = {
name: 'tom',
age:18,
}
readonly表示只读属性
interface Person{
name: string;
age: number;
readonly sex: number
}
let person: Person = {
name: 'tom',
age:18,
sex:1
}
person.sex = 0//报错
- 任意属性
!注意:设置任意属性时,其他属性的类型,必须是任意类型的子类型
interface Person{
name: string;
age: number;
[prop:string]:any
}
let person: Person = {
name: 'tom',
age:18,
county:'中国'
}
对类进行抽象
- 类实现接口,把类的共有特性,提取成接口,通过
implements实现
interface action {
run(): void,
jump(): void
}
class Person implements action{
run(){
console.log('run')
}
jump(){
console.log('jump')
}
}
let person = new Person()
person.run()
- 接口继承接口
interface Alarm {
alert(): void;
}
interface LightableAlarm extends Alarm {
lightOn(): void;
lightOff(): void;
}
- 接口继承类
class Point {
x: number;
y: number;
constructor(x: number, y: number) {
this.x = x;
this.y = y;
}
}
interface Point3d extends Point {
z: number;
}
let point3d: Point3d = {x: 1, y: 2, z: 3};
泛型
是指在定义函数、接口或类的时候,不预先指定具体的类型,而在使用的时候再指定类型的一种特性。
- 函数泛型
定义函数时泛指一个类型,调用时指定具体类型
function join<ABC>(first:ABC,second:ABC){
return `${first}${second}`
}
join<number,string>(1,'1');
- 数组泛型
let arr:Array<number>=[1,2,3,4]
- 类中的泛型
class Person<T>{
constructor(private data:T){
}
}
const person = new Person<string>('lxr')
```typescript
- 使用泛型作为具体的类型注解
```typescript
function hello<T>(params:T){
return params;
}
cosnt func:<T>(params:T)=>T=hello;
类型定义文件编写
在.d.ts文件中写类型定义文件
//定义全局变量
declare var $:(params:()=>void)=>void;
//定义全局函数,可以定义参数不同,函数名相同的函数(函数重载)
declare function $(params:()=>void):void;
declare function $(params:string):{html:(html:string)=>{}}
//定义对象,命名空间嵌套,定义类
declare namespace ${
namespace fn{
class init{
}
}
}
$.fn.init();
//接口语法
interface JQuery{
(Func:()=>void):void;
(selector:string):{html:(parans:string)=>{}}
}
var $:JQuery;
类的装饰器
- 对类进行修饰的函数,接收构造函数
- 通过@符号引用
- 类定义时候进行修饰,不是实例化
function testDecorator(){
//返回一个装饰器函数
return function<T extends new (...args:any[])=>{}>(constructor:T){
return class extends constructor{
name = 'L';
getName(){
return this.name;
}
};
}
}
// @testDecorator
const Test = testDecorator()(
class {
name:string;
constructor(name:string){
this.name = name;
}
}
)
const test = new Test('lxr')
console.log(test.getName())