TS从入门到出门
工作流程
安装(ts)
全局安装:
npm install -g typescript
编写(.ts文件)
编译(tsc 文件名.ts)
tsc hello.ts
会生成:
运行(node 文件名.js)
node hello.js
编译加运行
也可以将编译运行一起进行(使用ts-node):
安装:
npm install typescript ts-node @types/node@* -g
类型
类型注解
给变量添加类型约束。
数据类型
原有类型:
-
number
-
string
-
boolean
-
null
-
undefined
-
symbol
let a:number = 9; let b:string = 'wdy'; let c:boolean = true; let d:null = null; let e:undefined = undefined; let f:symbol = Symbol(); console.log(a,b,c,d,e,f) //9 wdy true null undefined Symbol() -
object
新增数据类型:
-
any
如果不知道一个变量属于什么类型,就使用any,使用any后,它可以是任何类型:
let a:any = 1 let b:any = '我是string'隐式any:声明变量不提供类型也不提供默认值或定义函数时,参数不指明类型:
在函数不声明返回值时,也会将返回值隐式声明为any:
在ts中过度使用any会使得ts失去意义,同时也会带来安全隐患。
-
unknown
提到any就不得不说unknown了:unknown类型也可以代表任何类型,但是使用unknown做任何事情都是不合法的(unknown不允许定义的值有任何操作):
unknown类型只允许赋值给any和unknown本身
-
数组
两种格式:变量名:类型名[] = [] 变量名:Array<类型名> = []
数组里面的类型必须提前说明,该数组只能存储规定类型数据:
let array1:number[] = [1,2,3,4,5]; array1.push(6); //❌ array1.push('wdy'); let array2:(number|string)[] = [1,2,3,'木头人']; array2.push(6); array2.push('wdy'); let array3:Array<number> = [1,2,3,4,5]; array3.push(6); //❌ array3.push('wdy'); let array4:Array<number|string> = [1,2,3,'木头人']; array4.push(6); array4.push('wdy'); console.log(array1); console.log(array2); console.log(array3); console.log(array4);
-
元组(Tuple)
可以将元组理解为限制数据类型与个数的数组:
let tup:[number,number,number,string] = [1,2,3,'木头人']; tup.push(1); tup.push('wdy'); console.log(tup) -
void
空类型,一般用于函数返回值。当函数没有返回值时或者只有return时使用:
function my():void{ return } function myy():void{ console.log('wdy') } -
枚举(Enum)
用于定义一些带有名字的常量,来描述特定的场景:
枚举名开头首字母大写
enum 枚举名{
常量名,
常量名
}
enum Week{ Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday } //数字递增 console.log(Week.Monday); console.log(Week.Tuesday); console.log(Week.Wednesday); //反向映射 console.log(Week[0]); console.log(Week[1]); console.log(Week[2]);
如果给元素赋初值:
```ts
enum Week{
Monday = 10,
Tuesday,
Wednesday,
Thursday,
Friday,
Saturday,
Sunday
}
//数字递增
console.log(Week.Monday);console.log(Week.Tuesday);console.log(Week.Wednesday);
//反向映射
console.log(Week[0]);console.log(Week[1]);console.log(Week[2]);
enum Weekk{
Monday = '周一',
Tuesday = '周二',
Wednesday ='周三',
Thursday = '周四',
Friday = '周五',
Saturday = '周六',
Sunday = '周日'
}
console.log(Weekk.Friday);console.log(Weekk.Saturday);console.log(Weekk.Sunday);
enum Weekkk{
Monday = 10,
Tuesday,
Wednesday,
Thursday = '周四',
Friday = '周五',
Saturday = '周六',
Sunday = '周日'
}
console.log(Weekkk.Monday);console.log(Weekkk.Thursday);
```
![image-20220719183827808]()
-
never
用户无法到达的类型:抛出异常,死循环等
联合类型
一个变量只能是一种数据类型吗?并不是——>联合类型:
联合类型代表我们可以将个变量设定为多种数据类型,这些数据类型之间使用|隔开。
交叉类型
将多个类型叠加使其成为一种新的类型,当我们使用该类型时,需要将多个类型全部实现:
type 类型名 = 类型&类型&类型
当出现同名参数时,如果类型相同则合并,如果类型不同则报错,
type My = {name:string} & {age:number};
let my:My = {
name:'wdy',
age:9
}
type Name = {name:string}
type Age = {age:string}
type Myy = Name & Age;
let myy:Myy = {
name:'wdy',
age:'9'
}
//❌
//age为number类型和string类型,判定认为age不能同时是number和string,所以会认为是never类型而报错。
type Boom = My & Myy;
let boom:Boom = {
name:'wdy',
age:9,
}
字面量类型
字面量不仅可以表示值,还可以表示类型即所谓的字面量类型:
当我们使用字面量类型时,变量的值和类型将不能在改变(与字面量类型保持一致),但可以将该变量赋给其他值:
//字符字面量
let a:'my' = 'my';
//数字字面量
let b:9 = 9;
//布尔字面量
let c:true = true;
let aa:string;
let bb:number;
let cc:boolean;
//❌
a = aa;
aa = a;
//❌
b = bb;
bb = b;
//❌
c = cc;
cc = c;
我们一般使用字面量类型来对值进行限制,这样可以使我们对于一些参数定义的更加具体:
type Name = 'wdy'|'WDY';
function my(name:Name,age:number):void{
console.log(`${name}${age}`);
}
my('wdy',9);
my('WDY',9);
my('WdY',9)
类型别名
别名即 (别的名字) 我们可以使用类型别名将类型名换成我们想要使用的名字:
格式:type 别名 = 类型
函数
声明
//函数声明
function add(a:number,b:number):number{
return a+b
}
console.log(add(10,10))
//函数表达式
const adding = function(a:number,b:number):number{
return a+b
}
console.log(adding(10,10))
//箭头函数
const my = ():string => {
return 'wdy'
}
console.log(my())
//无返回值
const mmy = ():void =>{
console.log('wdy')
}
mmy()
函数模板/函数别名
有时候我们需要声明较多结构相同的函数,这时候我们可以使用函数模板:
type 模板名 = (参数:参数类型) => 返回值类型
//定义函数模板,后续使用可以直接套用
type Add = (a:number,b:number) => number
const add : Add = (a,b)=>{
return a+b
}
console.log(add(10,10))
可选参数
可选参数即使用函数时,该参数可传可不传:
可选参数只能声明在必须参数后面
参数名?:数据类型
//可选参数,在参数名后面加?
function my(name:string,age?:number|string):void{
console.log(`${name} + ${age}`)
}
my('wdy',9);
my('wdy');
//❌
//可选参数只能声明在必须参数之后
function myy(name?:string,age:number|string):void{
console.log(`${name} + ${age}`)
}
默认值
带有默认值的参数也应该声明在必须参数后面
参数名:参数类型 = 默认值
function my(name:string,age:number|string = 9):void{
console.log(`${name} + ${age}`)
}
my('wdy',99);
my('wdy')
//❌
function myy(name:string = 'wdy',age:number|string):void{
console.log(`${name} + ${age}`)
}
myy('wdy',99);
myy(99)
对象
声明
对象中的属性在声明时,可以将属性设置为可选参数
const 对象名 :{
属性名1:属性类型,
属性名2?:属性类型,
方法名1(参数名:参数类型):返回值类型,
方法名2?:(参数名:参数类型)=>返回值类型
}={
属性名1:值1,
方法名1:方法体1
}
const my:{
name:string,
age?:number|string,
sayHi():string,
sayHello?:()=>void
}={
name:'wdy',
sayHi(){return 'Hello'}
}
console.log(my.name);
console.log(my.age);
console.log(my.sayHi());
console.log(my.sayHello())
对象模板/对象别名
type 模板名 = {
属性1:属性
属性2:属性
方法1:方法
}
type My = {
name:string,
age?:number|string,
sayHi():string,
sayHello?:()=>void
}
let my:My = {
name:'wdy',
age:9,
sayHi(){
return 'Hello'
}
}
console.log(my.name),
console.log(my.age),
console.log(my.sayHi())
接口
在面向对象的语言中,接口是一个十分重要的概念,它是对行为的抽象(类似于继承但有不完全等于继承):
interface 接口名{
属性:属性类型,
}
使用接口定义变量时,变量格式必须与接口格式相同,属性不能多也不能少:
interface My{
name:string,
age:number|string
}
let my:My = {
name:'wdy',
age:9
}
//❌
let myy:My = {
name:'wdy'
}
//❌
let myyy:My = {
name:'wdy',
age:9,
hobby:'sleep'
}
可选属性
那么,当我们对于一些属性的必要性不确定时应该怎么写呢?:
interface 接口名{
属性名?:属性类型
}
interface My{
name:string,
age?:number|string
}
let my:My = {
name:'wdy'
}
let my:My = {
name:'wdy',
age:9
}
任意属性
上面的两种方式我们只能按照接口的属性格式定义属性,如果我们想要新加属性呢?:
interface 接口名{
[propName:类型]:属性类型
}
一旦定义任意属性,其余确定属性和可选属性的类型必须是他的子集:
//❌
interface My{
name:string,
age?:number,
[propName:string]:string
}
interface Myy{
name:string,
age:number,
[propName:string]:string|number
}
let myy:Myy = {
name:'wdy',
age:9,
hobby:'sleep',
sex:0
}
只读类型
当我们希望一些类型在初始赋值后就不能被改变时,我们可以使用只读类型:
interface 接口名{
readonly:属性值:属性类型
}
interface My{
readonly name:string,
age:number,
}
let my:My = {
name:'wdy',
age:9
}
my.age = 99;
//❌
my.name = 'WDY'
类型推断
当我们不进行类型注解时,ts会根据上下文进行推断,从而得到变量类型,即在一些时候,我们可以不主动声明数据类型。
- 当我们声明一个变量,且赋予初始值时,该变量会自动推断其类型
- 当我们声明一个变量,未赋予初始值时,该变量会被推断为any类型,这也成为隐式any
- 当我们声明一个函数,未赋予初始值时,该函数如果没有返回值,会被推断为any,如果有返回值,会自动推断其返回值类型
let a = 123
let b = '123'
let c
c = 123;
c = '123'
console.log(typeof a)
console.log(typeof b)
function my(){
console.log('wdy')
}
function myy(name:string,age:number){
return `${name} ${age}`
}
my();
console.log(myy('wdy',9))
类型断言
当我们非常了解某个值时,我们可以使用类型断言,使用类型断言后,ts会按我们断言的类型通过编译:
有两种方式,在tsx中必须使用第二种:
<类型>变量名
变量名 as 类型
let a:string = 'wdy';
let lang:number = a.length
let b:string = 'wdy';
let langg:number = (<string>b).length
let c:string = 'wdy';
let langgg:number = (b as string).length
console.log(lang)
console.log(langg)
console.log(langgg)
当我们在使用接口声明变量时,可以使用类型断言避开检验:
interface My{
name:string,
age:number
}
//❌,没有根据接口的数据类型声明
let my:My = {
}
//使用类型断言避开检查
let myy:My = {
} as My
泛型
声明
在定义函数,接口或类时,不预先指定具体的类型,而是在使用的时候在指定类型:
function my<T>(name:T):T{
return name
}
console.log(my<string>('wdy'))
console.log(my('wdy'))
console.log(my<number>(123))
console.log(my(123))
function myy<T,U>(name:T,age:U):string{
return `${name} ${age}`
}
console.log(myy<string,number>('wdy',9));
console.log(myy<string,number|string>('wdy','9'))
泛型约束
在函数内部使用变量时,没有提前确定变量类型,因此我们不能直接使用其方法,这时,我们可以提前对泛型进行约束:
interface My{
length:number
}
//❌
function my<T>(name:T):T{
console.log(name.length)
return name
}
function myy<T extends My>(name:T):T{
console.log(name.length)
return name
}
console.log(myy<string>('wdy'))
//❌
console.log(myy<number>(99))
泛型接口
在泛型中也存在接口:
interface 接口名{
要求格式
}
interface Length{
length:number
}
interface Name{
<T extends Length>(name:T):T
}
let my:Name;
my = function<T extends Length>(name:T):T{
console.log(name.length)
return name
}
console.log(my('wdy'))
interface Namee<T>{
(name:T):T
}
let myy:Namee<any>;
myy = function<T>(name:T):T{
return name;
}
console.log(myy('wdy'))
类
声明
class My{
name:string;
age:number;
constructor(name:string,age:number){
this.name = name;
this.age = age
}
sayHello():void{
console.log('Hi')
}
}
let my = new My('wdy',9);
my.sayHello()
继承
继承类需要使用extends
子类声明构造函数需要在constructor内使用super
class My{
name:string;
age:number;
constructor(name:string,age:number){
this.name = name;
this.age = age
}
sayHello():void{
console.log('Hi')
}
}
class Myy extends My{
hobby:string
constructor(name:string,age:number,hobby:string){
super(name,age)
this.hobby = hobby
}
sayHi():void{
console.log('Hello')
}
}
let myy = new Myy('wdy',9,'sleep');
myy.sayHello();
修饰
在TS的类中共有三种修饰符:
| 修饰符 | 作用 |
|---|---|
| public | 默认使用,属性或方法为共有,所有地方均可访问 |
| private | 属性或方法为私有,不能在类外访问 |
| protected | 属性或方法受到保护,子类可以访问 |
class My{
public name:string;
private hobby:string;
protected age:number;
constructor(name:string,hobby:string,age:number){
this.name = name;
this.hobby = hobby;
this.age = age
}
private sayHobby():void{
console.log(this.hobby)
}
}
class Myy extends My{
constructor(name:string,hobby:string,age:number){
super(name,hobby,age)
//❌
console.log(this.hobby);
console.log(this.age)
}
}
let my = new My('wdy','sleep',9);
当我们类的构造函数也可以使用修饰符修饰:
| 修饰符 | 作用 |
|---|---|
| public | 默认 |
| private | 该类不允许被继承,也不允许被实例化 |
| protected | 该类只允许被继承,不允许被实例化 |