環(huán)境搭建
- npm i typescript -g
- 只需要安裝一次
- tsc --init
- 會(huì)生成tsconfig.json文件
- 打開配置文件中"outDir": "./",的注釋
- 根據(jù)需求修改
- tsc 文件名
- 執(zhí)行編譯生成js文件
- tsc --version 獲取版本號(hào)
VS半自動(dòng)化環(huán)境
- 終端
- 運(yùn)行任務(wù)
- tsc 監(jiān)視...
- 即可實(shí)現(xiàn)變化保存自動(dòng)編譯為js文件
也可以配置npm腳本實(shí)現(xiàn)
"build": "tsc",
"build:watch": "tsc --watch"
編譯指令
tsc 1.ts --outDir ./dist
如果不加--outDir則默認(rèn)會(huì)編譯到ts同一目錄
ts-node
各種規(guī)范(和ts無(wú)關(guān))
- AMD CMD require.js sea.js 都基本上過(guò)時(shí)放祟,不需要關(guān)心
- node commonjs commonjs2
- es6 module
- umd 兼容以上三種
tsconfig
"./src":只編譯src目錄下文件,內(nèi)部子文件夾不編譯
"./src//":代表遞歸編譯文件夾內(nèi)部所有子文件夾 后面代表所有文件
ts-node:其他類型編譯器 ts-node直接編譯
直接運(yùn)行
- vscode按照coderunner插件
- npm install ts-node -g
- 運(yùn)行即可
ts數(shù)據(jù)類型
數(shù)字,字符串杈女,布爾值
null underfined
它們倆是其他類型的子類型挣惰,可以賦值給其他類型
例如:let name:string=null;
但是需要打開:"strictNullChecks": false, 否則報(bào)錯(cuò)
不然就只能let name:string|null=null;
數(shù)組 元組 枚舉
void any Never
類型系統(tǒng)
string number boolean 基本類型
String Number Boolean 對(duì)象類型
基本類型可以賦值給包裝類型腾降,但是反之不可
數(shù)組(必須存儲(chǔ)同一類型)
//基本語(yǔ)法
//數(shù)組的聲明翁都,此時(shí)push是沒用的潮罪,因?yàn)槲炊x
// let arr:number[];
//數(shù)組的定義
let arr:number[]=[];
//泛型方式
// let arr1:Array<number>;
arr.push(...[1,2,4])
console.log(arr)
元組(類型不必相同)
長(zhǎng)度和類型都確定的數(shù)組姨谷,而且后面數(shù)據(jù)和前面類型必須一一對(duì)應(yīng)
let data:[number,string,boolean];
data=[1,"a",true];
說(shuō)明:元組再3.1之后逗宁,不能越界使用聯(lián)合類型了,而且賦值和定義的類型要一一對(duì)應(yīng)
聯(lián)合類型
//多個(gè)類型中的一個(gè) 或的關(guān)系
let a:string|number|boolean=20;
a="a";
console.log(a)
枚舉
enum Color{
RED,
YELLOW
}
console.log(Color.RED);//0
console.log(Color.YELLOW);//1
enum Color{
RED=1,
YELLOW
}
console.log(Color.RED);//1
console.log(Color.YELLOW);//2
//可以修改某個(gè)值然后后續(xù)的值順延即可
enum Week{
MONDAY=2,
TUESDAY
}
console.log(Week.TUESDAY);//3
- 常數(shù)枚舉
常數(shù)枚舉后續(xù)不可修改梦湘,所以在編譯成js時(shí)候直接輸出部分是0瞎颗,1數(shù)字而不是變量
const enum Colors{
Red,
Yellow
}
console.log(Colors.Red,Colors.Yellow); //0 1
- 枚舉的兼容性
枚舉類型與數(shù)字類型兼容件甥,并且數(shù)字類型與枚舉類型兼容;不同枚舉類型之間是不兼容的
Never
其他類型的子類型
說(shuō)明:代表那些永遠(yuǎn)不存在的值的類型哼拔,ts也可以自動(dòng)推斷引有,never此時(shí)也可以省略
返回值是never的函數(shù),永遠(yuǎn)不能正常結(jié)束倦逐,必須是類
似于拋出錯(cuò)誤這種譬正,也就代表著永遠(yuǎn)沒有返回值,異常不會(huì)正常走返回
function err():never{
throw new Error("error")
}
Any
說(shuō)明:任意類型,在不確定數(shù)據(jù)類型的情況下使用
let a:any="a";
a=10;
console.log(a)
類型綜合
/**
* 數(shù)據(jù)類型:
* 布爾類型(boolean)
* 數(shù)字類型(number)
* 字符串類型(string)
* 元組類型(tuple)
* 枚舉類型(enum)
* 任意類型(any)
* null和underfined
* void類型
* never類型:從不會(huì)出現(xiàn)的值
*/
//不賦值檬姥,也不會(huì)有默認(rèn)值
let flag:boolean=true
//第一種定義數(shù)組的方式
let arrs:number[]=[1,2,3]
//第二種定義數(shù)組的方式
let arrs1:Array<number>=[1,2,3]
//第三種定義數(shù)組的方式
let arrs4:Array<any>=[1,'3',true] //不會(huì)報(bào)錯(cuò)
//元組類型:屬于數(shù)組的一種,此時(shí)數(shù)據(jù)類型和后面賦值要一一對(duì)應(yīng)
let arrs2:[number,string]=[123,'this is ts']
//枚舉類型
enum WEEK{
success=1, //指定枚舉從1開始曾我,不指定則默認(rèn)0起始
error,//此時(shí)不指定,則為2健民,如果多個(gè)值中間指定賦值抒巢,則后面的依此加一
'underfined'=-1,
'null'=-2
}
let w:WEEK=WEEK.error;
console.log(WEEK.underfined); //-1
//任意類型
let num:any=123;
num='asdas';
//任意類型使用場(chǎng)景
let item:any=document.getElementById('test');
//null和underfined是其他(never)數(shù)據(jù)類型的子類型
let num1:undefined;
// console.log(num1) //不報(bào)錯(cuò),如果定義為number類型則報(bào)錯(cuò)了
let num2:undefined|number;
// console.log(num2);//兼具兩者的優(yōu)勢(shì)
function run():void{
}
//聲明never的變量只能被never類型賦值:代表從不會(huì)出現(xiàn)的值
let a:never;
a=(()=>{
throw new Error('錯(cuò)誤')
})()
函數(shù)
//函數(shù)表達(dá)式
let f:()=>string=function():string{
return "a"
}
let f:()=>void=function(){
}
//函數(shù)聲明
function fn(x:number,y:number):number{
return x+y
}
type
type用來(lái)定義類型或者類型別名
type GetUserName = (firstName: string, lastName: string) => {name:string};
let getUserName: GetUserName = function (firstName: string, lastName: string): {name:string} {
return {name:firstName+lastName};
}
- 泛型類型別名(擴(kuò)展)
type Cart<T>={list:T[]}|T[]; //聯(lián)合類型
let c1:Cart<string>={list:['1']};
let c2:Cart<string>=['1']
可選參數(shù)和參數(shù)默認(rèn)值
說(shuō)明:通過(guò)?來(lái)定義可選參數(shù)
function fn(x:Type,y?:Type):Type
可選參數(shù)默認(rèn)為undefined
可選參數(shù)必須在必傳參數(shù)之后
//可選參數(shù)
function fn(x:number,y?:number):number{
return x+y
}
//參數(shù)默認(rèn)值秉犹,其實(shí)因?yàn)轭愋屯茖?dǎo)蛉谜,可直接寫成y=1
function fn1(x:number,y:number=1):number{
return x+y
}
console.log(fn(1));//NaN
console.log(fn1(1));//2
補(bǔ)充:可選參數(shù)和默認(rèn)值不要用在一個(gè)參數(shù)上
剩余參數(shù)
//剩余參數(shù)
function fn2(...arg:any[]){
console.log(arg) //[ 1, 2, 3 ]
}
fn2(1,2,3)
function sum(...numbers:Array<number>) {
//accu是最終要返回的值
return numbers.reduce((accu,item)=>accu+item,0);
}
函數(shù)重載
//注意:這三行除了注釋之外,必須緊緊的貼在一起凤优,否則報(bào)錯(cuò)
//定義函數(shù)的重載格式
function fn(x:number,y:string);
function fn(x:number,y:number);
//定義函數(shù)具體的實(shí)現(xiàn)
function fn(x:any,y:any){
console.log(x+y)
}
fn(1,2) //3
fn(1,"2")//12
- 函數(shù)參數(shù)的協(xié)變
不推薦這么使用悦陋,了解即可
type logFunc=(a:number|string)=>void;
let log:logFunc;
function log1(a:number|string|boolean) {
}
log=log1;//正確
//其實(shí)很好理解,ts不認(rèn)類型筑辨,只要包含即可俺驶,明顯log1更多選擇可以賦值給更小選擇的
函數(shù)綜合
//函數(shù)聲明
function run1():void{
}
//匿名函數(shù)
let run2=function():number{
return 123;
}
let fun3=function(name:string,age:number):string{
return name+'---'+age;
}
// console.log(fun3('zq',12));
//可選參數(shù):必須是再最后面,不能再前面
function fun4(name:string,age?:number):void{}
fun4('zq')
//默認(rèn)參數(shù):可選參數(shù)可以再默認(rèn)參數(shù)之前
function fun5(name?:string,age:number=20):void{}
fun5('zq')
//剩余參數(shù)
function fun6(...res:number[]):number{
let sum=0;
for (let index = 0; index < res.length; index++) {
sum+=res[index];
}
return sum;
}
// console.log(fun6(1,2,3,4,5,6));//21
//剩余參數(shù)形式二
function fun7(a:number,...res:number[]):number{
let sum=0;
for (let index = 0; index < res.length; index++) {
sum+=res[index];
}
return sum;
}
// console.log(fun7(1,2,3,4,5,6));//20
//es5中出現(xiàn)同名函數(shù)棍辕,下面會(huì)替換上面的暮现,即使參數(shù)不同
//函數(shù)重載:必須要有一個(gè)any的實(shí)現(xiàn)
function func(name:string):string;
function func(age:number):number;
function func(str:any):any{
if(typeof str==='string'){
return '我叫: '+str
}else{
return '我的年齡是: '+str
}
}
// console.log(func('a'),func(12));我叫: a 我的年齡是: 12
ts中的this
案例一:
let obj={
a:10,
fn(){
//函數(shù)中默認(rèn)this指向是any,通過(guò)下面再配置文件中解決楚昭,而且如果是類似于
//document中事件的this栖袋,ts會(huì)自動(dòng)推導(dǎo)出類型,this指向不需要下面配置也是事件對(duì)象
// "noImplicitThis": true
console.log(this.a)
//注意:此時(shí)this的指向只是是否有提示的問(wèn)題抚太,真的執(zhí)行代碼配置文件設(shè)置不設(shè)置值都是10
}
}
案例二:
let obj={
a:20,
fn(this:Document){
console.log(this.querySelector)
}
}
document.onclick=obj.fn
說(shuō)明:如果配置配置了this指向"noImplicitThis": true,
則fn中的this指向就是obj對(duì)象塘幅,但是此時(shí)obj.fn指向了
點(diǎn)擊事件,為了有提示信息尿贫,需要手動(dòng)指定this指向this:Document
這個(gè)this參數(shù)其實(shí)是一個(gè)假參數(shù)电媳,ts編譯時(shí)候會(huì)被去掉,純粹是
為了代碼提示而存在庆亡,this指向配置不配置匾乓,修改不修改都不影響最后的結(jié)果
修飾符
public protected(該類和子類能訪問(wèn)) private(類,對(duì)象內(nèi)部) readonly(類又谋,對(duì)象內(nèi)部可用拼缝,其他只讀)
案例一:
class Person{
readonly n:number;
constructor(num:number){
this.n=num;
}
}
let p=new Person(20)
案例二:簡(jiǎn)寫方式
class Person{
constructor(public num:number){
this.n=num;
}
}
let p=new Person(20)
說(shuō)明:因?yàn)閠s不同于js娱局,構(gòu)造函數(shù)中屬性需要先聲明才能
使用,此時(shí)public num:number此種方式就是相當(dāng)于提前再
class中先聲明了一份
存取器
class Person{
//私有屬性咧七,大家默認(rèn)的規(guī)則是下劃線
private _num:number;
//存取器
//存取器再ts中不是當(dāng)做方法使用的衰齐,而是被當(dāng)做屬性
get num():number{
return this._num
}
set num(num:number){
if(num>0){
this._num=num;
}
}
}
let p=new Person()
p.num=-10
console.log(p.num);//undefined
靜態(tài)
class Person {
private static instance;
private constructor() { }
public static getInstance() {
if (!Person.instance) {
Person.instance = new Person();
}
return Person.instance;
}
}
let p=Person.getInstance();//相等
let p1=Person.getInstance();
//注意:靜態(tài)屬性和靜態(tài)方法都可被子類繼承
抽象類
抽象描述一種抽象的概念,無(wú)法被實(shí)例化猪叙,只能被繼承娇斩;無(wú)法創(chuàng)建抽象類的實(shí)例,抽象方法不能在抽象類中實(shí)現(xiàn)穴翩,只能在抽象類的具體子類中實(shí)現(xiàn),而且必須實(shí)現(xiàn)锦积。
abstract class Person {
constructor() { }
abstract study():void;
}
class Student extends Person{
study(): void {
console.log("學(xué)習(xí)");
}
}
let s=new Student();
s.study()
重載和重寫
- 重寫是指子類重寫繼承父類中的方法
- 重載是指為同一個(gè)函數(shù)提供多個(gè)類型定義
class Animal {
speak(word:string){
console.log('a');
}
}
class Cat {
//重寫
speak(word:string){
console.log('b');
}
}
重載其實(shí)就是函數(shù)重載
類綜合
class Person{
private name:string;
constructor(n:string){
this.name=n;
}
run():void{
console.log(this.name);
}
}
/**
* 類中屬性修飾符
* public: 公有 都可以訪問(wèn),默認(rèn)值
* protected: 保護(hù)類型 在該類和子類能訪問(wèn)
* private: 在該類能訪問(wèn)
*/
class Student extends Person{
//實(shí)際上在新版本ts芒帕,構(gòu)造函數(shù)在此時(shí)可省略,方法執(zhí)行依然正常
// constructor(n:string){
// super(n)
// }
static sex='男';//靜態(tài)屬性
//靜態(tài)方法里面只能使用靜態(tài)屬性
static print(){
console.log("靜態(tài)方法",this.sex);
}
//重寫父類方法
run():void{
console.log("重寫方法");
}
}
let s=new Student('zq');
// s.run() //zq ,不重寫父類方法的情況下
// Student.print()
// s.run() //輸出: 重寫方法 重寫父類的方法
/**
* 抽象類:
* 提供其他類繼承的基類丰介,不能實(shí)例化
* abstract關(guān)鍵字定義抽象類和抽象方法背蟆,抽象類中的抽象方法不包含具體實(shí)現(xiàn)并且必須在派生類中實(shí)現(xiàn)
*
* 抽象類中可以有屬性,構(gòu)造函數(shù)哮幢,和非抽象方法
*/
abstract class Animal {
name:string;
constructor(name:string){
this.name=name;
}
abstract eat():any;
run(){
console.log(this.name+" 跑步");
}
}
class Dog extends Animal{
eat() {
console.log("狗吃飯");
}
}
let d=new Dog('pf');
d.eat();
d.run();//pf 跑步
接口
- 用來(lái)描述對(duì)象的屬性以及類型
interface Point{
x:number;
y:number;
}
let ponit:Point={x:0,y:0};
- 描述行為的抽象
//接口不能有任何屬性和方法實(shí)現(xiàn)带膀,只能有抽象描述
interface Options{
num:number;
//可選的
name?:string;
say();
}
class optImpl implements Options{
num: number;
constructor(num:number){
this.num=num;
}
say() {
console.log(this.num+"說(shuō)話");
}
}
function fn(opts:Options) {
opts.say()
}
fn(new optImpl(20))
- 類可以實(shí)現(xiàn)多個(gè)接口,但是只能繼承一個(gè)父類
接口補(bǔ)充
- 接口的readonly
interface Circle{
readonly PI:number;
}
let circle:Circle={
PI:3.14
}
// circle.PI=0; 無(wú)法分配到 "PI" 橙垢,因?yàn)樗侵蛔x屬性
- 索引簽名(任意屬性)
/**
* 索引簽名:
* 希望規(guī)則是:一組由數(shù)字進(jìn)行key命名的對(duì)象
* 補(bǔ)充:索引簽名的key類型只能是string或者number
* 索引簽名在下面?zhèn)鲄r(shí)候垛叨,是可有可沒有的,而且不限制個(gè)數(shù)
*/
interface Options{
//key是number柜某,value是any類型的數(shù)據(jù)
[atrr:number]:any;
}
function fn(opts:Options) {
}
fn({
0:1,
2:20
})
namespace a{
interface PlainObject{
[propNmae:string]:number;//key value形式嗽元;這樣可以任意多個(gè)值
}
let obj:PlainObject={
x:1,
y:2,
z:3
}
//這數(shù)組進(jìn)行約束,因?yàn)閿?shù)組的key其實(shí)就是索引喂击,所以本質(zhì)還是key-value形式
interface UserInterface{
[index:number]:string;
}
let arr:UserInterface=['1','2','3']
}
- 接口約束構(gòu)造函數(shù)
使用new來(lái)約束構(gòu)造函數(shù)
interface WithNameClass{
new(name:string):Animal;
}
class Animal{
//public name:string 就相當(dāng)于 this.name=name了
constructor(public name:string){}
}
//使用
function createAnimal(clazz:WithNameClass,name:string) {
return new clazz(name);
}
createAnimal(Animal,'zq');
斷言
interface Options{
num:number;
name:string;
}
function fn(opts:Options) {
}
//斷言
//按理說(shuō)必須傳入{num:20,name:"呵呵"}類似的才能通過(guò)
//但是通過(guò)斷言可強(qiáng)制判定傳入?yún)?shù)是什么類型
fn({} as Options)
補(bǔ)充:
let obj={
num:10,
name:"saa",
a:1
}
fn(obj)
說(shuō)明:如果把傳入的參數(shù)先賦值好在傳入剂癌,可以避免規(guī)則檢測(cè)
不會(huì)報(bào)錯(cuò),但是此種情況只能在傳入的obj覆蓋全部所需參數(shù)
情況下翰绊,也就是說(shuō)只能多不能少
接口的兼容性
ts跟類型沒有關(guān)系佩谷,只和有沒有有關(guān)系;簡(jiǎn)單說(shuō):我包含你的监嗜,我就可以給你這種類型傳參
interface Animal{
name:string;
}
interface Person{
name:string;
speak:(words:string)=>void;
}
function getNmae(animal:Animal) {
console.log(animal.name);
}
let p:Person={
name:'zq',
speak(){
}
}
getNmae(p);
//此處傳p也正確谐檀,因?yàn)锳nimal有的Person都有,所以也符合規(guī)則
函數(shù)類型接口
/**
* 函數(shù)類型接口
* 是一個(gè)包含由fn并且值的類型為函數(shù)的結(jié)構(gòu)體
* 并不是描述函數(shù)結(jié)構(gòu)而是一個(gè)包含函數(shù)的對(duì)象結(jié)構(gòu)
*/
interface Options{
fn:Function
}
let o:Options={
fn:function(){
}
}
/**
* 下面約定就是函數(shù)結(jié)構(gòu)秤茅,而不是包含有函數(shù)的對(duì)象結(jié)構(gòu)了
*/
interface IFn{
(x:number):number
}
let fn:IFn=function(x:number){return x}
/**
* 下面是函數(shù)結(jié)構(gòu)的實(shí)踐
* 因?yàn)閕nterface的約定稚补,保證了傳參的正確性
* 在編譯階段避免了出錯(cuò)
*/
interface MouseEventCallBack{
(e:MouseEvent):any;
}
let fn1:MouseEventCallBack=function(e:MouseEvent){}
document.onclick=fn1;
補(bǔ)充案例
interface AjaxData{
code:number;
data:any;
}
interface AjaxCallBack{
(rs:AjaxData):any
}
function ajax(callback:AjaxCallBack){
callback({
code:200,
data:{}
})
}
泛型
泛型是指在定義函數(shù)、接口或類的時(shí)候框喳,不預(yù)先指定具體的類型课幕,而是在使用的時(shí)候再指定類型的一種特性厦坛;泛型T作用域只限于函數(shù)內(nèi)部使用
/**
* 泛型:
* 很多時(shí)候,類型寫死乍惊,不利于復(fù)用
*/
//泛型變量
function fn<T>(args:T):T{
return args;
}
function fn1<T,S>(args:T,args1:S):[T,S]{
return [args,args1];
}
//數(shù)組形式
function fn2<T>(args:T[]):T[]{
return args;
}
function fn3<T>(args:Array<T>){}
泛型類
class MyArray<T>{
private _data:T[]=[];
public push(v:T):number{
return this._data.length;
}
}
let a=new MyArray<string>();
a.push("a")
let b=new MyArray<number>();
b.push(1)
泛型類型
//泛型類型
let fn:<T>(x:T,y:T)=>number=function(x,y){
return Number(x)+Number(y)
}
let fn1=function<T,S>(x:T,y:S):number{
return Number(x)+Number(y)
}
console.log(fn(1,2));//3
console.log(fn1<number,string>(1,"2"));//3
泛型接口
interface IFN<T,S>{
(x:T,y:S):S
}
let fn2:IFN<string,number>=function(x,y){
return Number(x)+Number(y);
}
接口泛型
很奇怪的用法杜秸,不太常用
interface Calculate{
<T>(a:T,b:T):T
}
let add:Calculate=function<T>(a:T,b:T):T{
<!--報(bào)錯(cuò),因?yàn)閭魅腩愋筒灰欢梢韵嗉?->
<!--return a+b; -->
return a;
}
add<number>(1,2);
- (泛型可以有多個(gè))面試題:不增加中間變量的情況下润绎,交換兩個(gè)變量的值
function swap<A,B>(tuple:[A,B]):[B,A] {
return [tuple[1],tuple[0]];
}
swap<string,number>(['zq',10]);
默認(rèn)泛型類型
function swap<A=number,B=string>(tuple:[A,B]):[B,A] {
return [tuple[1],tuple[0]];
}
swap([1,'zq']);
// swap<string,number>(['zq',1]);
類類型
/**
* 類類型:
* 表示這個(gè)類型對(duì)應(yīng)的對(duì)象
*/
//錯(cuò)誤實(shí)例:此時(shí)Array代表就是類類型撬碟,但是需要的參數(shù)是該類
//對(duì)應(yīng)的構(gòu)造函數(shù)
function getArray1(constructor:Array<number>){
return new constructor();
}
getArray2(Array)
//補(bǔ)充:p后面的Person就是類類型
let p:Person=new Person();
//下面是構(gòu)造函數(shù)
let fn1:{new ():Person}
//正確寫法
function getArray2(constructor:{new():Array<string>}){
return new constructor();
}
getArray2(Array)
泛型約束
形式一:
function fn<T extends number>(a:T){
console.log(a);
}
形式二:
interface Len{
length:number
}
function fn1<T extends Len>(a:T){
console.log(a.length);
}
fn1("a")//此時(shí)在fn1(1)則會(huì)報(bào)錯(cuò),因?yàn)閿?shù)字類型沒有l(wèi)ength屬性
接口綜合
//ts自定義方法傳入?yún)?shù) 對(duì)json進(jìn)行約束
function printLabel(labelInfo:{label:string}):void{
console.log("printlabel");
}
// printLabel('name')//錯(cuò)誤
// printLabel({name:'haha'})//錯(cuò)誤
printLabel({label:'haha'}) //正確
//一莉撇、函數(shù)類型接口
/**
* 以上只是針對(duì)單一方法進(jìn)行約束呢蛤,那么批量約束呢
* 1. 屬性接口 對(duì)json的約束
*/
interface FullName{
firstName:string;
secondName:string;
}
function printName(name:FullName){
console.log(name.firstName,name.secondName);
}
let obj={
age:20,
firstName:'zq',
secondName:'pf',
}
//注意: 傳入對(duì)象引用,只要包含必須項(xiàng)即可棍郎,但是如果直接傳入對(duì)象其障,則不能包含多余屬性
//但是建議,不要添加多余屬性
printName(obj)
/**
* 2. 可選屬性
*/
interface FullName1{
firstName:string;
secondName?:string; //可選屬性
}
function printName1(name:FullName1){
console.log(name.firstName,name.secondName);
}
printName1({firstName:'zq'})//zq undefined
/**
* 3.函數(shù)類型接口
* 對(duì)方法的傳入?yún)?shù)和返回值進(jìn)行約束
*/
interface encrypt{
(key:string,value:string):string;
}
let md5:encrypt=function(key:string,value:string):string{
return 'haha';
}
//1. 可索引接口:數(shù)組涂佃,對(duì)象的約束(不常用)
interface UserArr{
[index:number]:string;
}
let arr:UserArr=['aaa','bbb']
interface UserObj{
[index:string]:string;
}
let obj1:UserObj={name:'20'}
//2. 類類型接口: 對(duì)類的約束和抽象類有點(diǎn)相似
interface Animal1{
name:string;
eat1(str:string):void;
}
class Dog1 implements Animal1{
name: string;
constructor(name:string){
this.name=name;
}
eat1(str: string): void {
console.log(this.name,str);
}
}
let d1=new Dog1('zq');
d1.eat1('haha'); //zq haha
//1. 接口擴(kuò)展:接口繼承其他接口
interface A1{
eat():void;
}
interface A2{
fly():void;
}
//接口可以多繼承
interface P1 extends A1,A2{
run():void
}
//類只能單繼承励翼,但是可以多實(shí)現(xiàn),而且繼承和實(shí)現(xiàn)可并存
class P2 implements P1{
run(): void {
}
eat(): void {
}
fly(): void {
}
}
泛型綜合
// 泛型基本使用
function getData<T>(name:T):T{
return name;
}
console.log(getData('zq'));
console.log(getData(12));
//泛型類
class MinClass<T>{
public list:T[]=[]
add(value:T):void{
this.list.push(value)
}
min():T{
let min=this.list[0];
return min;
}
}
let m=new MinClass<number>();
// 泛型接口
//1. 方式一
interface ConfinFn{
<T>(v1:T):T;
}
let fn:ConfinFn=function<T>(v1:T):T{
return v1;
}
fn<string>('haha')
//2. 方式二
interface ConfinFn1<T>{
(v1:T):T;
}
function fn2<T>(v1:T):T{
return v1;
}
let fn3:ConfinFn1<string>= fn2;
fn3('sad')
/**
*泛類 把類當(dāng)作參數(shù)的泛型類
*/
class User{
//之所以加underfined是因?yàn)椴怀跏蓟瘓?bào)錯(cuò)辜荠,再多加一個(gè)類型就不報(bào)錯(cuò)了
username:string|undefined;
password:string|undefined;
}
//這樣就可以把各種外部類傳遞進(jìn)來(lái)
class MysqlDB<T>{
add(user:T):boolean{
return true;
}
}
let m1=new MysqlDB<User>();
// m1.add(new User())
命名空間和模塊
export.ts
export namespace B {
export let url = 'safas';
export function getData(): any[] {
return [
1, 2, 3
]
}
// export {url,getData} 一次統(tǒng)一暴露汽抚,但是使用此方式,上面的export就不能存在了
// export default getData 一個(gè)模塊只能用一次,引入方式也有區(qū)別
}
export namespace C{
export let url1 = 'safas';
export function getData1(): any[] {
return [
1, 2, 3
]
}
}
modules.ts
/**
* 命令空間: 內(nèi)部模塊伯病,主要用于組指代碼造烁,避免命名沖突(其實(shí)針對(duì)的就是一個(gè)文件同名方法的沖突解決方案)
* 模 塊 : ts的外部模塊的簡(jiǎn)稱,側(cè)重代碼復(fù)用狱从,一個(gè)模塊里可能有多個(gè)命令空間
*/
import { B,C } from './export';
// import getData from './export'; export default方式導(dǎo)出的時(shí)候的引入方式
//如果導(dǎo)出時(shí)候沒有命名空間 則可以通過(guò)as取別名
import { getData as get } from './export';
B.getData();
裝飾器
- 方式一:配置文件(開啟裝飾器):"experimentalDecorators": true
- 方式二:根據(jù)vscode錯(cuò)誤提示(裝飾器錯(cuò)誤提示)也可以自動(dòng)修改
/**
* 裝飾器:
* 類裝飾器 屬性裝飾器 方法裝飾器 參數(shù)裝飾器
*
* 裝飾器寫法:
* 普通裝飾器(無(wú)法傳參)
* 裝飾器工廠(可傳參)
*
* 通俗的講:裝飾器就是一個(gè)方法
*/
//1. 類裝飾器
namespace A{
function logclass(params:any){
//params其實(shí)就是被裝飾的類
// console.log(params);
//擴(kuò)展屬性
params.prototype.apiURL='www.zq.com';
params.prototype.run=function(){
console.log('run');
}
}
@logclass
class HttpClient{
getData(){
}
}
let h:any=new HttpClient();
// console.log(h.apiURL); //www.zq.com
// h.run()
}
//2. 類裝飾器(帶參數(shù))
namespace B{
function logclass(params:string){
//此時(shí)params就是hello
//target就是被裝飾的類
return function(target:any){
// console.log(params,target);
}
}
//裝飾的過(guò)程是直接執(zhí)行的膨蛮,不需要實(shí)例化,不需要調(diào)用
@logclass('hello')
class HttpClient{
getData(){
}
}
let h=new HttpClient();
}
/**
* 重載構(gòu)造的小例子
* 類的裝飾器表達(dá)式會(huì)在運(yùn)行時(shí)當(dāng)作函數(shù)被調(diào)用季研,類的構(gòu)造函數(shù)作為其唯一的參數(shù)
* 如果類的裝飾器返回一個(gè)值敞葛,它會(huì)使用提供的構(gòu)造函數(shù)來(lái)替換類的聲明
*/
namespace C{
function logclass(target:any){
return class extends target{
apiUrl='修改路徑';
getData(){
console.log('修改前');
this.apiUrl='修改路徑'+'-----';
console.log(this.apiUrl);
}
}
}
@logclass
class HttpClient{
apiUrl:string|undefined;
constructor(){
this.apiUrl='路徑初始化'
}
getData(){
console.log(this.apiUrl);
}
}
let h=new HttpClient();
// h.getData() //修改前 修改路徑-----
}
/**
* 屬性裝飾器:
* 屬性裝飾器表達(dá)式會(huì)在運(yùn)行時(shí)當(dāng)作函數(shù)被調(diào)用,傳入下列2個(gè)參數(shù):
* 1. 對(duì)于靜態(tài)成員來(lái)說(shuō)是類的構(gòu)造函數(shù)(例如 :Person)与涡,對(duì)于實(shí)例成員是類的原型對(duì)象(例如:Person.prototype)
* 2. 成員的名字
*/
namespace D{
function logurl(params:string){
return function(target:any,propertyName:string){
// console.log(target,propertyName);
//雖然這樣可修改惹谐,但是通過(guò)打印發(fā)現(xiàn)target[propertyName]是undefined,很奇怪
//而且必須這個(gè)屬性如果在構(gòu)造函數(shù)有賦值,構(gòu)造函數(shù)內(nèi)部賦值會(huì)替換裝飾器的賦值驼卖,因?yàn)闃?gòu)造是后執(zhí)行的
target[propertyName]=params;//相當(dāng)于用@logurl('xxx')的參數(shù)給apiUrl重新賦值
//注意:后面還有Object.defineProperty的形式實(shí)現(xiàn)的氨肌,不直接這么 target[propertyName]=params賦值;因?yàn)橛械氖菦]有參數(shù)的酌畜,需要?jiǎng)討B(tài)監(jiān)聽
}
}
class HttpClient{
@logurl('xxx')
apiUrl:string|undefined;
getData(){
console.log(this.apiUrl);
}
}
let h=new HttpClient();
// h.getData()
}
/**
* 方法裝飾器:
* 會(huì)被應(yīng)用到方法的屬性描述符上怎囚,可以用來(lái)監(jiān)視,修改或者替換方法定義
* 方法裝飾器參數(shù):
* 1. 對(duì)于靜態(tài)成員來(lái)說(shuō)是類的構(gòu)造函數(shù),對(duì)于實(shí)例成員是類的原型對(duì)象:{getData: ?, constructor: ?}
* 2. 成員的名字(方法名稱): "getData"
* 3. 成員的屬性描述符: {value: ?, writable: true, enumerable: true, configurable: true};
* 重點(diǎn): 另外屬性描述器的value屬性就是指向被裝飾的方法
*/
//方式裝飾器一
namespace D{
function logMethods(params:any){
return function(target:any,methodsName:string,desc:PropertyDescriptor){
// console.log(target,methodsName,desc);
//擴(kuò)展屬性和方法
target.apiUrl=params;
target.run=function(){
console.log(this.apiUrl);
}
}
}
class HttpClient{
@logMethods('www')
getData(){
console.log('執(zhí)行');
}
}
let h:any=new HttpClient();
// h.getData();//執(zhí)行
// h.run();//www
}
//方法裝飾器二
namespace E{
function logMethods(params:any){
return function(target:any,methodsName:any,desc:PropertyDescriptor){
/**
* ? () {
console.log('執(zhí)行');
}
*/
// console.log(desc.value);
//修改裝飾器的方法恳守,把裝飾器方法里面?zhèn)魅氲膮?shù)修改為string類型
//1. 保存當(dāng)前方法
let om=desc.value;
desc.value=function(...args:any[]){
console.log("執(zhí)行原始方法前");
args=args.map((value)=>{
return String(value)
})
//如果需要綁定上下文考婴,甚至傳參到原始方法
om.apply(this,args);
console.log("執(zhí)行原始方法后");
}
//如果不涉及上下文調(diào)用原始的getData方法,則可以把方法調(diào)用放在外面
// om();
}
}
class HttpClient{
@logMethods('www')
getData(...args:any[]){
args.forEach(element => {
console.log(element);
});
}
}
let h:any=new HttpClient();
// h.getData(1,2,3,4);
/**
* 輸出結(jié)果:
* 執(zhí)行原始方法前
1
2
3
4
執(zhí)行原始方法后
*/
}
/**
* 方法參數(shù)裝飾器:用處不大催烘,可能有復(fù)雜用法沥阱,但是基本可以通過(guò)類裝飾器替代,不必研究
* 參數(shù)裝飾器表達(dá)式會(huì)在運(yùn)行時(shí)當(dāng)作函數(shù)被調(diào)用伊群,可以使用參數(shù)裝飾器為類的原型增加一些元素?cái)?shù)據(jù)考杉,
* 參數(shù)列表:
* 1. 對(duì)于靜態(tài)成員來(lái)說(shuō)是類的構(gòu)造函數(shù)(Person),對(duì)于實(shí)例成員是類的原型對(duì)象:{getData: ?, constructor: ?} 例如:Person.prototype
* 2. 方法的名字: "getData"
* 3. 參數(shù)在函數(shù)參數(shù)列表中索引 : 0
*/
//? 代表可選參數(shù):可傳可不傳
function logParams(params?:any){
return function(target:any,methodsName:string,index:number){
// console.log(target,methodsName,index);
target.apiURL=params;//擴(kuò)展屬性
}
}
class HttpClient{
getData(@logParams('xxx') uuid:any){
console.log(uuid);
}
}
let h:any=new HttpClient();
h.getData();
/*
* 1. 屬性和方法裝飾器舰始,誰(shuí)先寫誰(shuí)先執(zhí)行
* 2. 方法裝飾器又分為方法的和方法參數(shù)的崇棠,先參數(shù)后方法
* 3. 最后是類裝飾器
* 4. 同類型的,先執(zhí)行后寫的蔽午,從內(nèi)到外
*/
- 屬性裝飾器和方法裝飾器
namespace a1{
function upperCase(target:any,prototyName:string) {
let value=target[prototyName];
const getter=()=>value;
const setter=(newVal:string)=>{
value=newVal.toUpperCase();
}
delete target[prototyName];//刪掉之前的屬性描述器
Object.defineProperty(target,prototyName,{
get:getter,
set:setter,
enumerable:true,
configurable:true
})
}
//方法的
function methodEnumerable(params:boolean) {
//propertyDescriptor:老的屬性描述器
return function(target:any,prototyName:string,propertyDescriptor:PropertyDescriptor) {
propertyDescriptor.enumerable=params;
}
}
class Person {
@upperCase
name:string='zq';
@methodEnumerable(true)
public getName(){
}
}
let p=new Person();
console.log(p.name);//ZQ
for (const attr in p) {
console.log(attr);
}
}
類型保護(hù)
- 類型保護(hù)就是一些表達(dá)式易茬,他們?cè)倬幾g的時(shí)候就能通過(guò)類型信息確保某個(gè)作用域內(nèi)變量的類型
- 類型保護(hù)就是能通過(guò)關(guān)鍵字判斷出分支中的類型
以下是案例:
- typeof
//最主要就是再對(duì)應(yīng)判斷里面有對(duì)應(yīng)函數(shù)的提示
function double(input: string | number | boolean) {
if (typeof input==='string') {
input.toLowerCase();
}else if (typeof input==='number') {
input.toFixed(2);
}else{
input;
}
}
- instanceof
重點(diǎn)是加了判斷,代碼提示也有了及老,而且不會(huì)提示多余的屬性
class Animal {
public name:string='zq';
}
class Bird extends Animal{
public swing:number=2;
}
function getName(a:Animal) {
if (a instanceof Bird) {
a.swing;
} else {
a.name;
}
}
- 鏈?zhǔn)脚袛噙\(yùn)算符
//先判斷a是不是null/underfined,如果是則直接返回null或underfined,否則返回a.b的值
a?.b
- 可辨識(shí)的聯(lián)合類型
利用聯(lián)合類型中的共有字段進(jìn)行類型保護(hù)的一種技巧范抓,相同字段的不同取值就是可辨識(shí)
interface WarningButton{
class:'warning',
text1:'1'
}
interface DangerButton{
class:'danger',
text2:'11'
}
type Button=WarningButton|DangerButton;
function getButton(btn:Button) {
//直接寫骄恶,代碼只提示共有屬性class
//如下才行
if (btn.class==='warning') {
btn.text1;
}else{
btn.text2;
}
}
- in操作符
interface WarningButton{
text1:'1'
}
interface DangerButton{
text2:'11'
}
function getButton(btn:WarningButton|DangerButton) {
if ('text1' in btn) {
btn.text1;
} else {
btn.text2;
}
}
- 自定義的類型保護(hù)
自定義類型保護(hù),其實(shí)就是定義了一個(gè)函數(shù)匕垫,函數(shù)的返回值是一個(gè)類型的謂詞僧鲁,形式是params is Type;params必須是當(dāng)前函數(shù)簽名里面的一個(gè)參數(shù)名
interface WarningButton{
name1:'1',
text1:'1'
}
interface DangerButton{
name2:'2',
text1:'11'
}
function isWarning(x:WarningButton|DangerButton):x is WarningButton {
//規(guī)則自定義
return x.text1==='1';
}
function getButton(btn:WarningButton|DangerButton) {
if (isWarning(btn)) {
btn.name1;
} else {
btn.name2;
}
}
類型變換
- 交叉類型
//多個(gè)類型的疊加象泵,并且的關(guān)系
let b:string&number=
interface Bird{
name:string;
fly():void;
}
interface Person{
name:string;
eat():void;
}
//交叉類型其實(shí)就是兩個(gè)接口類型的屬性的并集
type BirdMan=Bird&Person;
- typeof
可以獲取一個(gè)變量的類型
/*
type Person={
name:string;
age:number
}
*/
let p={
name:'zq',
age:10
}
type Person=typeof p;
let p2:Person={
name:'qq',
age:20
}
- 索引訪問(wèn)操作符
通過(guò)[]獲取一個(gè)類型的子類型
interface Person{
name:string;
job:{
name:string
}
}
let n:Person['job']['name']='fe';
- keyof
索引類型查詢操作符
interface Person {
name: string;
gender: 'male' | 'female';
//方式一:添加任意屬性
// [propName:string]:any;
}
//報(bào)錯(cuò):因?yàn)闊o(wú)法確定傳入的key寞秃,可能Person里面沒有這個(gè)key,所以return報(bào)錯(cuò)
// function getValueKey(val: Person, key: string): any {
// return val[key];
// }
//方式二
// type PKeys='name'|'gender';
type PKeys=keyof Person; //返回一個(gè)接口key的集合偶惠;這樣避免修改key之后也要修改此處
function getValueKey(val: Person, key: PKeys): any {
return val[key];
}
let p: Person = {
name: 'zq',
gender: 'female'
};
getValueKey(p,'gender');
- 映射類型
再定義的時(shí)候用in操作符批量定義
interface Person {
name: string;
gender: 'male' | 'female';
}
type PartialPerson={
//? 代表可選 變成賦值的時(shí)候不需要全部都寫了
[key in keyof Person]?:Person[key]
}
let p: PartialPerson = {
name: 'zq',
// gender:'female' 可以不寫春寿,因?yàn)槌闪丝蛇x的
};
內(nèi)置工具類型
TS中內(nèi)置了一些工具類型來(lái)幫助我們更好的使用類型系統(tǒng),雖然下面案例都是使用接口的,但是實(shí)際上class也可以的
- Partial
將傳入的屬性由非可選變成可選
interface Person {
name: string;
gender: 'male' | 'female';
}
let p: Partial<Person> = {
name: 'zq'
};
實(shí)現(xiàn)原理
type Partial<T>={
[key in keyof T]?:T[key]
}
- Required
將傳入的屬性中的可選項(xiàng)變?yōu)楸剡x項(xiàng)
interface Person {
name: string;
gender?: 'male' | 'female';
}
let p: Required<Person> = {
name: 'zq',
gender:'female'
};
實(shí)現(xiàn)原理
type Required<T>={
//注意這里是-?
[key in keyof T]-?:T[key]
}
- ReadOnley
屬性變成只讀
interface Person {
name: string;
gender?: 'male' | 'female';
}
type ReadOnlyPerson=Readonly<Person>
let p: ReadOnlyPerson = {
name: 'zq',
gender:'female'
};
// p.gender='zq';無(wú)法分配到 "gender" 忽孽,因?yàn)樗侵蛔x屬性绑改。
實(shí)現(xiàn)原理
type Readonly<T>={
Readonly [key in keyof T]:T[key]
}
- Pick
從傳入的屬性中摘取某
一項(xiàng)
返回
interface Person {
name: string;
gender?: 'male' | 'female';
}
type PickPerson=Pick<Person,'name'>;
let p: PickPerson = {
name: 'zq'
};
實(shí)現(xiàn)原理
//keyof T=name|gender 是個(gè)聯(lián)合類型
type Pick<T,K extends keyof T>={
[key in K]:T[key]
}
- 映射類型修飾符的控制
TS中增加了對(duì)映射類型修飾符的控制,具體而言兄一,一個(gè)readonly或厘线?修飾符里可以用前綴+或-來(lái)表示這個(gè)修飾符應(yīng)該被添加或移除。TS中部分內(nèi)置工具類型利用了這個(gè)特性(Partial...)
條件類型
再定義泛型的時(shí)候能夠添加進(jìn)邏輯分支出革,泛型使用更靈活
案例一
interface A{
name1:string
}
interface B{
name2:string
}
interface C{
name3:string
}
interface D{
name4:string
}
//判斷T是不繼承A
//ts判斷是否繼承是根據(jù)屬性造壮,所以即使傳進(jìn)入是A,因?yàn)閷傩远加新钍砸部梢?type Condition<T>=T extends A?B:C;
let c:Condition<A>={name2:'zq'};
案例二
interface A{
name1:string
}
interface B{
name2:string
}
interface C{
name3:string
}
interface D{
name4:string
}
type Condition<T>=T extends A?B:C;
//其實(shí)最終效果就是 B和C的交集
let c:Condition<A|D>={name2:'zq',name3:'zq1'};
TS內(nèi)置了很多條件類型
- Exclude
從T可分配的類型中排除U
type E=Exclude<string|number,string>;
let e:E=10;
- Extract
從T可分配的類型中提取U
type E=Extract<string|number,string>;
let e:E='1';
- NonNullable
從T中排除null和underfined
type E=NonNullable<string|number|null|underfined>;
let e:E='hello';
let e:E=10;
- ReturnType
獲取函數(shù)類型的返回類型耳璧,并作為新的類型
function getUserInfo() {
return {name:'zq',age:10}
}
type UserInfo=ReturnType<typeof getUserInfo>
let u:UserInfo={name:'zq',age:20};
- InstanceType
獲取構(gòu)造函數(shù)的實(shí)例類型
class Person {
name:string;
constructor(name:string) {
this.name=name;
}
}
type P=InstanceType<typeof Person>
let p:P={name:'zq'};
let p:P=new Person('zq');
類型聲明
- 聲明文件可以讓我們不需要將js重構(gòu)為TS成箫,只需要加上聲明文件就可以使用系統(tǒng)
- 類型聲明再編譯的時(shí)候都會(huì)被刪除,不會(huì)影響真正的代碼
- 可以把類型聲明放在一個(gè)單獨(dú)的類型聲明文件中
- 文件命令規(guī)范為*.d.ts
- @types是一個(gè)約定前綴楞抡,所有第三方聲明的類型庫(kù)都會(huì)有這樣的前綴(例如@types/node @types/jquery)
- TS核心庫(kù)的類型聲明文件
- 一般*.d.ts文件都在typings文件夾下面
使用案例
//jquery.d.ts
declare function jQuery(selector: string): any;
declare namespace jQuery {
function ajax(url: string): void;
}
//需要導(dǎo)出伟众,否則外部無(wú)法使用
export default jQuery;
聲明示例
declare let age: number;
declare function getName(): string;
declare class Animal {
naem: string
}
declare interface Person {
name: string
}
//declare內(nèi)部不需要加declare
declare namespace jQuery{
function ajax(url:string):void;
let name:string;
namespace fn{
function extend(object:any):void;
}
}
注意:vscode下一般都可以找到,但是我們需要是的特定目錄下找到定義文件召廷,所以可以通過(guò)tsconfig.json文件實(shí)現(xiàn)凳厢。
補(bǔ)充
- 無(wú)法重新聲明塊范圍變量“name”
在ts中有個(gè)lib.dom.ts;類似于全局文件竞慢,里面聲明的有name屬性先紫,所以這樣直接寫不行,需要把該ts文件轉(zhuǎn)換成模塊
- 代碼里面有export import之類的代碼筹煮,那么這個(gè)文件就變成一個(gè)模塊
- ts為dom提供了一整套類型聲明
let root: HTMLElement | null = document.getElementById('root');
//!強(qiáng)行斷言 root不是null,這樣就不會(huì)報(bào)錯(cuò)了遮精,否則root可能為null,說(shuō)白了欺騙編譯器
root!.style.color = 'red';
- 包裝對(duì)象 (java中的裝箱拆箱)
let name1: string = 'zf';
// name1.toLowerCase();
let name11 = new String(name1);
name11.toLowerCase();
let isOk1: boolean = true;
let isOk2: boolean = Boolean(1); //拆箱為boolean
// let isOk3:boolean=new Boolean(1); 報(bào)錯(cuò) 對(duì)象賦值給了基本類型
自動(dòng)在基本類型和對(duì)象類型之間切換;基本類型上沒有方法;在內(nèi)部迅速的完成一個(gè)裝箱的操作败潦,把基本類型迅速包裝成對(duì)象類型本冲,然后用對(duì)象來(lái)調(diào)用方法
- 類型斷言
let name2: string | number;
此時(shí)直接調(diào)用name2只能調(diào)用string和number的共同方法;要么調(diào)用前賦值,然后回自動(dòng)推導(dǎo)劫扒;要么就使用類型斷言檬洞,就可以正常調(diào)用方法了;(name2 as string).toLowerCase();
- 字面量類型
后面賦值只能是'boy'或'girl'
; let G1:'boy'|'girl';
- 使用注意
關(guān)鍵字 | 作為類型使用 | 作為值使用 |
---|---|---|
class | yes | yes |
enum | yes | yes |
interface | yes | no |
type | yes | no |
function | no | yes |
var,let,const | no | yes |
例如:interface接口就不能作為值賦值,一般使用最多的就是定義類型