1、TypeScript是什么?
TypeScript 是一种由微软开发的自由和开源的编程语言。它是 JavaScript 的一个超集,而且本质上向这个语言添加了可选的静态类型和基于类的面向对象编程。 在线TypeScript演练场
1.1 TypeScript与javascript区别
1.2 JavaScript 数据类型
String、Number、Boolean、Object(Array、Function)、Symbol、undefined、null
1.3 TypeScript 新增数据类型
void、any、never、元组、枚举、高级类型
2、基础类型
字符串类型
我们使用 string
表示文本数据类型。 和 JavaScript 一样,可以使用双引号 "
或单引号 '
表示字符串, 反引号 ` 来定义多行文本和内嵌表达式。
let str: string = 'abc'
数字类型
和 JavaScript 一样,TypeScript 里的所有数字都是浮点数。这些浮点数的类型都是 number
。 除了支持十进制和十六进制字面量,TypeScript 还支持 ECMAScript 2015 中引入的二进制和八进制字面量。
let a: number = 6 //十进制
let hexA: number = 0xf00d //十六进制
let binaryA: number = 0b1010 //二进制
let octalA: number = 0o744 //八进制
布尔类型
我们使用 boolean
表示布尔类型,表示逻辑值 true
/ false
。
let bool: boolean = true
Null & Undefined
null
表示对象值缺失,undefined
表示未定义的值。
let un: undefined = undefined
let nu: null = null
void
用于标识方法返回值的类型,表示该方法没有返回值。
function noReturn (): void {
console.log('No return value')
}
const a =():void=>{console.log('No return value')}
- undefined 并不是保留字段可以被赋值,所以设置undefined时,建议使用 void 0
任意类型any
声明为 any
的变量可以赋予任意类型的值。
let x: any
x = 1
x = 'a'
x = {}
let arr: any[] = [1, 'a', null]
never
never
类型表示的是那些永不存在的值的类型。 例如,never
类型是那些总是会抛出异常或根本就不会有返回值的函数表达式或箭头函数表达式的返回值类型;变量也可能是 never
类型,当它们被永不为真的类型保护所约束时。
never
类型是任何类型的子类型,也可以赋值给任何类型;然而,没有类型是 never
的子类型或可以赋值给 never
类型(除了 never
本身之外)。 即使 any
也不可以赋值给 never
。
// 返回never的函数必须存在无法达到的终点
function error(message: string): never {
throw new Error(message);
}
- 类型推断:变量在声明时并未赋值,类型推断为
any
若其他类型需要被赋值为 null
或 undefined
时, 在 tsconfig.json 中将 scriptNullChecks 设置为 false。或者 使用联合类型。
Symbol
symbol
类型的值是通过 Symbol 构造函数来创建
let s: symbol = Symbol()
3、复杂类型
数组 array
定义数组的方式有二种:
第一种:可以在元素类型后加上 []
第二种:可以使用数组泛型 Array<元素类型>
。 此外,在元素类型中可以使用联合类型。 符号 |
表示或。
let arr1: number[] = [1, 2, 3]
let arr2: Array<number> = [1, 2, 3]
let arr3: Array<number | string> = [1, 2, 3, 'a']
//数组的元素必须是规定好的类型,否则就会报错
let a:number[] = [1,2,3,'4'] // error '4'string不是number类型
元组 tuple
用来表示已知元素数量和类型的数组,各元素的类型不必相同,对应位置的类型必须相同。
let tuple:[string,number] = ['ali',18]
可以调用数组 push
方法添加元素,但并不能读取新添加的元素。
tuple.push('a')
console.log(tuple) // ['ali',18, "a"]
tuple[2] // Error: Tuple type '[number, string]' of length '2' has no element at index '2'.
对象 object
直接 let a: object;
是不是没有什么意义,因为 js
中对象太多了
我们可以这样来用
let obj:{age:number,is:boolean};
obj = {
age:18,
is:true,
}
属性必须在类型 { name: string; age: number; }
中
函数 funtion
我们要规定函数的 输入类型 和 返回类型
()
里面是输入的形参的 类型
=>
代表是 返回值 的类型
:
后面的都是声明类型,和代码逻辑没有什么关系
传入多余的参数会报错,可选参数用?
表示,注意:可选参数后不能再加参数了
//第一种写法
function sum(a:number,b:number,c?:number):number{
return a+b;
}
//第二种写法
const sum = (a:number,b:number):number=>a+b
console.log(sum(1,2,3))
用 interface
来描述函数类型
注意一点 之前用的 =>
来表示返回值类型
这里是在 ()
后 : 返回值类型
interface Isum{
(a:number,b:number):number;
}
const newSum:Isum = sum
接口 interface
它能很方便的帮我们定义 Ojbect
类型,可以灵活的处理属性多个类型及是否必选存在/可选/只读
在 interface
属性中
?
表示不是必要的属性
|
表示联合类型:确定是哪几种类型后可用这个
readonly
不可改变的,定义完后就不能修改,跟 const
有点像,不过 const
是针对变量, readonly
是针对属性
interface Iobj {
a:string|number;//可以是字符串或者数值
readonly age:number;
love?:string;
}
let obj:Iobj = {
a:'jom',
age:18,
}
obj.age = 30 //Cannot assign to 'age' because it is a read-only property
联合类型 union types
对于一个变量的类型可能是几种类型的时候我们可以使用 any
,但是 any
的范围是不是有点大了,尽量避免使用,
我们如果知道是其中的哪几种类型的话,我们就可以使用 联合类型 用 |
分隔
注意:在没有赋值之前,只能访问共同的方法、属性,比如下面的例子,number
没有length
属性
let a:number|string;
a.length //Property 'length' does not exist on type 'string | number'.\
Property 'length' does not exist on type 'number'
a='haha';
a=1;
断言 type inference
在上面例子中,我们声明了一个属性类型为 number | string
它不能调用 length
方法
这是机器没法判断这个类型,但是我们可以指定类型 string
这里我们就可以用到 类型断言
类型断言有两种形式: <类型>
, as
function sum(a:number|string):void{
const str = a as string //as
const str = <string>a //<>
console.log(str.length)
}
两种形式是等价的。 但当你在TypeScript里使用JSX时,只有 as
语法断言是被允许的。
类型守卫 type guard
遇到联合类型的时候,使用 类型守卫可以 缩小范围
实现以下和上面一样的方法
function sum(a:number|string):void{
if(typeof a ==='string'){
console.log(a.length)
}
}
类型守卫 除了 typeof
之外 ,还有 instanceof
、 in
4、枚举
我们使用 enum
表示枚举类型。 枚举成员值只读,不可修改。 一般适用于声明一个常量
数字枚举
数字枚举支持反向映射
初始值为 0, 逐步递增,也可以自定义初始值,之后根据初始值逐步递增。
enum fiveLamp {
white,
red,
}
console.log(fiveLamp.white) // 0
console.log(fiveLamp[1]) // 'red'
console.log(five)//{ "0": "white", "1": "red", "white": 0, "red": 1 }
字符串枚举
字符串枚举不支持反向映射
enum Message {
Success = '成功',
Fail = '失败'
}
常量枚举
在枚举关键字前添加 const
,该常量枚举会在编译阶段被移除。
const enum Month {
Jan,
Feb,
Mar
}
let month = [Month.Jan, Month.Feb, Month.Mar]
编译后:
"use strict";
var month = [0 /* Jan */, 1 /* Feb */, 2 /* Mar */]; // [0 /* Jan */, 1 /* Feb */, 2 /* Mar */]
外部枚举
外部枚举(Ambient Enums)是使用 declare enum
定义的枚举类型。
declare enum Month {
Jan,
Feb,
Mar
}
let month = [Month.Jan, Month.Feb, Month.Mar]
编译后:
"use strict";
let month = [Month.Jan, Month.Feb, Month.Mar];
declare
定义的类型只会用于编译时的检查,编译结果中会被删除。所以按照上述例子编译后的结果来看,显然是不可以的。因为 Month 未定义。
declare
和const
可以同时存在
5、类
在 ES6
中就有 类的概念了,在 TS
中对类添加一些功能
下面是个简单的例子,定义了一个Parent的父类,再定义了它的一个子类Child
3个访问修饰符:
Public
:修饰的属性或方法是共有的 在 任何地方 都能访问
Private
:修饰的属性或方法是私有的 只有 本类 中访问
Protected
:修饰的属性或方法是受保护的 在 本类 和 子类中 能够访问
比如指定父类中 age
访问权限为 private
,只能在 Parent
访问,子类访问会出错
class Parent {
public name:string
private age:number
constructor(name:string){
this.name = name
}
play(){
console.log(`${this.name}在玩`)
}
}
class Child extends Parent{
private child_age:number
constructor(name:string){
super(name)
this.child_age = super.age;//报错Property 'age' is private and only accessible within class 'Parent'.
}
}
我们可以设置访问权限为 protected
,这样子类就能访问
静态属性 static
上面的 name
age
这两个属性都是通过 实例 去访问的
使用 static
修饰的属性是通过 类 去访问,是每个实例共有的
同样 static
可以修饰 方法,用 static
修饰的方法称为 类方法,可以使用类直接调用
class Parent {
static name:string
}
console.log(Parent.name)
只读 readonly
我们给属性添加上 readonly
就能保证该属性只读,不能修改,如果存在 static
修饰符,写在其后
class Parent {
static readonly name:string = 'hahaha'
}
Parent.name='哈哈哈哈哈哈'//Cannot assign to 'name' because it is a read-only property.
抽象类 abstract
TS
新增的抽象类,不能被实例化,适用于直接使用该类创建实例(不能被new)只能被继承
在 class
前面 添加 abstract
修饰符,
在抽象类中 可以写 抽象方法 ,抽象类没有方法体
举个例子:一个动物的抽象类,有个叫的方法,不可能 每个动物的叫声一样吧,我们可以把它设置为抽象方法,具体功能子类进行实现(该怎么叫就由子类来写)
abstract class Animal {
public name:string
constructor(name:string){
this.name = name
}
abstract barking():void
}
class Cat extends Animal{
constructor(name:string){
super(name)
}
barking(){
console.log('喵~')
}
}
6、接口继承接口
- 跟类继承类一样,相同的对象难免是可以继承的,接口也可以继承接口,连接方式也是使用
extends
。
interface addIce{
ice:string
}
interface addSugar extends addIce{
sugar:string
}
let milk:addSugar={
ice:'少冰',
sugar:'半糖'
}
- 上述例子中定义了两个接口分别是
addIce
addSugar
,而addSugar
继承了addIce
的ice
属性,所以在使用addSugar
接口的时候形状必须一致。 - 在
milk
变量中因为使用了addSugar
接口,所以属性如果不符合形状就会报错。
7、接口继承类
- 在
TypeScript
中我们可以使用接口来继承类。
class MilkTea {
ice:string
sugar:string
constructor(ice:string,sugar:string){//constructor是一个构造方法,用来接收参数
this.ice = ice;
this.sugar = sugar;
};
}
interface doSthing extends MilkTea{
}
let milk:doSthing={
ice:'少冰',
sugar:'半糖'
}
-
在上面的例子中,我们的
interface
接口规定了形状,而这个形状本身是没有东西的,但是它继承了MilkTea
类的ice
和sugar
属性,所以就规定了使用这个接口interface
的变量需要有ice
和sugar
属性。 -
如上我们的
milk
给了它一个接口doSthing
,所以这个变量需要有ice
和sugar
属性。 -
接口跟类又不是同一种东西那怎么可以继承呢?虽然这两不是一个东西,但是我们在定义一个
class
的时候其实也是定义了一种类型。 -
就拿上面的
MilkTea
类来说,我们创建这个类的时候其实也是创建了一个MilkTea
的类型,我们可以这样使用。
class MilkTea {
ice:string
sugar:string
constructor(ice:string,sugar:string){//constructor是一个构造方法,用来接收参数
this.ice = ice;
this.sugar = sugar;
};
}
let milk:MilkTea={
ice:'少冰',
sugar:'半糖'
}
-
这样是没有问题的,所以当我们
doSthing
接口继承时其实是集成了MilkTea
这个类型,而这个类型大家是不是觉得很熟悉,他就是interface
的写法类似啊,所以我们继承了一个类实际上就是继承了一个接口。 -
值得一提的是,接口继承类的时候,只会继承它的实例属性和实例方法,继承不了构造函数。
8、泛型
泛型就像一个占位符一个变量,在使用的时候我们可以将定义好的类型像参数一样传入,原封不动的输出
不用泛型的话,这个函数可能是下面这样:
function identity(arg: number): number {
return arg;
}
或者,我们使用any
类型来定义函数:
function identity(arg: any): any {
return arg;
}
使用any
类型会导致这个函数可以接收任何类型的arg
参数,这样就丢失了一些信息:传入的类型与返回的类型应该是相同的,所以可按下面方式:
这里 T
是相当于一个占位符,在方法(变量,接口....等等)后面添加 <T>
function identity<T>(arg: T): T{
return arg;
}
多个参数也是类似写法
function identity<T,U>(arg:[T,U]):[T,U]{
return arg;
}
//在使用的时候,聪明的就判断出 传入的类型,并修改了 `T`,`U`
identity(['1',1])
也可以使用 interface
来约束 泛型
在 T
后面 extends Ilen
,定义 Ilen
里面代码表示,T
必须要有 length
属性
这样在 方法里面调用 params.length
就不会报错
interface Ilen{
length:number;
}
function getLenght<T extends Ilen>(params:T):number{
return params.length
}
getLenght('sffsdf');
getLenght(11); //报错Argument of type 'number' is not assignable to parameter of type 'Ilen'
在类中使用泛型
class Cat<T>{
eat(name:T){
console.log('这只小猫吃了'+name)
}
}
let newCat = new Cat<string>()
newCat.eat('老鼠')
newCat.eat(111)//报错
在接口中使用泛型
interface Iobj<T,U>{
key:T
value:U
}
const a:Iobj<string,number> = {key:'admin',value:1}
const b:Iobj<string,boolean> = {key:'admin',value:false}
9、类型别名type
type a = string|number
let b:a = 11
10、交叉类型&
把类型都组合起来,变量赋值必须满足 交叉类型
interface Iobj{
key:number;
}
type newObj = Iobj&{value:string}
let a:newObj = {key:1,value:'hhh'}