一:TypeScript的配置文件:
https://blog.csdn.net/weixin_46221198/article/details/125589781
√ 標(biāo)記為常用配置
常用配置
"include": ["./src/*"],//src目錄下的編譯為js,,支持支持 glob 模式匹配√
//"files":["a.ts"],//指定具體的文件(只能是文件),需編譯的文件較少時(shí)使用√
"exclude":["./data/*"]//data目錄下的不不便宜為js√
"compilerOptions": {
"incremental": true, // TS編譯器在第一次編譯之后會(huì)生成一個(gè)存儲(chǔ)編譯信息的文件,第二次編譯會(huì)在第一次的基礎(chǔ)上進(jìn)行增量編譯,可以提高編譯的速度 √(增量編譯)
"tsBuildInfoFile": "./buildFile", // 增量編譯文件的存儲(chǔ)位置
"diagnostics": true, // 打印診斷信息
"target": "ES5", // 目標(biāo)語言的版本
"module": "CommonJS", // 生成代碼的模板標(biāo)準(zhǔn)
"outFile": "./app.js", // 將多個(gè)相互依賴的文件生成一個(gè)文件,可以用在AMD模塊中,即開啟時(shí)應(yīng)設(shè)置"module": "AMD",
"lib": ["DOM", "ES2015", "ScriptHost", "ES2019.Array"], // TS需要引用的庫,即聲明文件,es5 默認(rèn)引用dom、es5、scripthost,如需要使用es的高級版本特性,通常都需要配置,如es8的數(shù)組新特性需要引入"ES2019.Array",
"allowJS": true, // 允許編譯器編譯JS,JSX文件 √
"checkJs": true, // 允許在JS文件中報(bào)錯(cuò),通常與allowJS一起使用
"outDir": "./dist", // 指定輸出目錄 √
"rootDir": "./", // 指定輸出文件目錄(用于輸出),用于控制輸出目錄結(jié)構(gòu) √
"declaration": true, // 生成聲明文件,開啟后會(huì)自動(dòng)生成聲明文件
"declarationDir": "./file", // 指定生成聲明文件存放目錄
"emitDeclarationOnly": true, // 只生成聲明文件,而不會(huì)生成js文件
"sourceMap": true, // 生成目標(biāo)文件的sourceMap文件
"inlineSourceMap": true, // 生成目標(biāo)文件的inline SourceMap,inline SourceMap會(huì)包含在生成的js文件中
"declarationMap": true, // 為聲明文件生成sourceMap
"typeRoots": [], // 聲明文件目錄,默認(rèn)時(shí)node_modules/@types
"types": [], // 加載的聲明文件包
"removeComments":true, // 刪除注釋 √
"noEmit": true, // 不輸出文件,即編譯后不會(huì)生成任何js文件
"noEmitOnError": true, // 發(fā)送錯(cuò)誤時(shí)不輸出任何文件
"noEmitHelpers": true, // 不生成helper函數(shù),減小體積,需要額外安裝,常配合importHelpers一起使用
"importHelpers": true, // 通過tslib引入helper函數(shù),文件必須是模塊
"downlevelIteration": true, // 降級遍歷器實(shí)現(xiàn),如果目標(biāo)源是es3/5,那么遍歷器會(huì)有降級的實(shí)現(xiàn)
"strict": true, // 開啟所有嚴(yán)格的類型檢查 √
"alwaysStrict": true, // 在代碼中注入'use strict' √
"noImplicitAny": true, // 不允許隱式的any類型 √
"strictNullChecks": true, // 不允許把null、undefined賦值給其他類型的變量
"strictFunctionTypes": true, // 不允許函數(shù)參數(shù)雙向協(xié)變
"strictPropertyInitialization": true, // 類的實(shí)例屬性必須初始化
"strictBindCallApply": true, // 嚴(yán)格的bind/call/apply檢查
"noImplicitThis": true, // 不允許this有隱式的any類型
"noUnusedLocals": true, // 檢查只聲明、未使用的局部變量(只提示不報(bào)錯(cuò))
"noUnusedParameters": true, // 檢查未使用的函數(shù)參數(shù)(只提示不報(bào)錯(cuò))
"noFallthroughCasesInSwitch": true, // 防止switch語句貫穿(即如果沒有break語句后面不會(huì)執(zhí)行)
"noImplicitReturns": true, //每個(gè)分支都會(huì)有返回值
"esModuleInterop": true, // 允許export=導(dǎo)出,由import from 導(dǎo)入
"allowUmdGlobalAccess": true, // 允許在模塊中全局變量的方式訪問umd模塊
"moduleResolution": "node", // 模塊解析策略,ts默認(rèn)用node的解析策略,即相對的方式導(dǎo)入
"baseUrl": "./", // 解析非相對模塊的基地址,默認(rèn)是當(dāng)前目錄
"paths": { // 路徑映射,相對于baseUrl
// 如使用jq時(shí)不想使用默認(rèn)版本,而需要手動(dòng)指定版本,可進(jìn)行如下配置
"jquery": ["node_modules/jquery/dist/jquery.min.js"]
},
"rootDirs": ["src","out"], // 將多個(gè)目錄放在一個(gè)虛擬目錄下,用于運(yùn)行時(shí),即編譯后引入文件的位置可能發(fā)生變化,這也設(shè)置可以虛擬src和out在同一個(gè)目錄下,不用再去改變路徑也不會(huì)報(bào)錯(cuò)
"listEmittedFiles": true, // 打印輸出文件
"listFiles": true// 打印編譯的文件(包括引用的聲明文件)
}
二:聯(lián)合類型和類型保護(hù)(類型斷言)
類型保護(hù):就是一些表達(dá)式或者關(guān)鍵字在編譯時(shí)候就能確定在某個(gè)作用域內(nèi)變量的類型.
類型斷言:通過類型斷言這種方式可以告訴編譯器,“相信我,我知道自己在干什么”。 類型斷言好比其它語言里的類型轉(zhuǎn)換,但是不進(jìn)行特殊的數(shù)據(jù)檢查和解構(gòu)。 它沒有運(yùn)行時(shí)的影響,只是在編譯階段起作用。 TypeScript 會(huì)假設(shè)你,程序員,已經(jīng)進(jìn)行了必須的檢查。
//聯(lián)合類型和類型保護(hù)
interface Bird {
fly: boolean;
sing: () => {};
}
interface Dog {
fly: boolean;
bark: () => {};
}
function trainAnimal(animal: Bird | Dog) {//聯(lián)合類型
animal.sing();//報(bào)錯(cuò)
}
這里不能確認(rèn)animal上是否有sing方法,這里我們需要進(jìn)行類型保護(hù)
//方法一:類型斷言
function trainAnimal(animal: Bird | Dog) {
//聯(lián)合類型
if (animal.fly) {
(animal as Bird).sing();
} else {
(animal as Dog).bark();
}
}
//方法二 in語法判斷的方式
function trainAnimalSecond(animal: Bird | Dog) {
if ("sing" in animal) {
animal.sing();
} else {
animal.bark();
}
}
//instanceof 語法判斷
class Num {
count: number;
constructor(num: number) {
this.count = num;
}
}
//instanceof 語法判斷
function addSecond(first: object | Num, second: object | Num) {
if (first instanceof Num && second instanceof Num) {
return first.count + second.count;
}
return 0;
}
三:枚舉
枚舉:允許開發(fā)者定義一組命名的常量。使用枚舉可以使其更容易記錄意圖,或創(chuàng)建一組不同的情況。TypeScript提供了基于數(shù)字和字符串的枚舉。
1 數(shù)值型枚舉
我們首先從數(shù)字枚舉開始,如果你來自其他語言,可能會(huì)更熟悉它。一個(gè)枚舉可以用 enum 關(guān)鍵字來定義。
enum Direction {
Up = 1,
Down,
Left,
Right,
}
上面,我們有一個(gè)數(shù)字枚舉,其中 Up 被初始化為 1 ,所有下面的成員從這一點(diǎn)開始自動(dòng)遞增。換句話說,Direction.Up的值是 1 ,Down 是 2,Left是3,Right是4。
如果我們愿意,我們可以完全不使用初始化器:
enum Direction {
Up,
Down,
Left,
Right,
}
這里,Up的值是0,Down是1,依次類推。這種自動(dòng)遞增的行為對于我們可能不關(guān)心成員值本身,但關(guān)心每個(gè)值與同一枚舉中的其他值不同的情況很有用。
使用枚舉很簡單:只需將任何成員作為枚舉本身的一個(gè)屬性來訪問,并使用枚舉的名稱來聲明類型:
enum UserResponse {
No = 0,
Yes = 1,
}
function respond(recipient: string, message: UserResponse): void {
// ...
}
respond("Princess Caroline", UserResponse.Yes);
數(shù)字枚舉可以混合在計(jì)算和常量成員中(見下文)。簡而言之,沒有初始化器的枚舉要么需要放在第一位,要么必須放在用數(shù)字常量或其他常量枚舉成員初始化的數(shù)字枚舉之后。換句話說,下面的情況是不允許的:
enum E {
A = getSomeValue(),
B,
// ? Enum成員必須有初始化器。
}
2 字符串枚舉
字符串枚舉是一個(gè)類似的概念,但有一些細(xì)微的運(yùn)行時(shí)差異,如下文所述。在一個(gè)字符串枚舉中,每個(gè)成員都必須用一個(gè)字符串字頭或另一個(gè)字符串枚舉成員進(jìn)行常量初始化。
enum Direction {
Up = "UP",
Down = "DOWN",
Left = "LEFT",
Right = "RIGHT",
}
雖然字符串枚舉沒有自動(dòng)遞增的行為,但字符串枚舉有一個(gè)好處,那就是它們可以很好地 "序列化"。換句話說,如果你在調(diào)試時(shí)不得不讀取一個(gè)數(shù)字枚舉的運(yùn)行時(shí)值,這個(gè)值往往是不透明的--它本身并不傳達(dá)任何有用的意義(盡管 反向映射往往可以幫助你),字符串枚舉允許你在代碼運(yùn)行時(shí)給出一個(gè)有意義的、可讀的值,與枚舉成員本身的名稱無關(guān)。
四.泛型:generic泛指的類型,指的是在定義函數(shù)/接口/類型時(shí),不預(yù)先指定具體的類型,而是在使用的時(shí)候在指定類型限制的一種特性。
//函數(shù)中的泛型:限制入?yún)?返回值
function connect<Common>(first: Common, second: Common) {
return `${first}${second}`;
}
connect<string>("1", "2");
connect<number>(1, 2);
function join<Common>(first: Common, second: Common):T {
return first;
}
//------------------------------------------------------
//多個(gè)泛型
function map<T, P>(first: T, second: P) {
return `${first}${second}`;
}
map<string, number>("1", 2);
map(1,2); //默認(rèn)類型推斷--<number,number>
類中的泛型:
// class DataManager {
// constructor(
// private item1: string[] | number[],
// private item2: string[] | number[]
// ) {} //類型聯(lián)合
// getItem(index: number) {
// return this.item1[index];
// }
// }
//當(dāng)有大量的參數(shù)需要聯(lián)合類型時(shí),我們可以使用泛型來進(jìn)行代碼優(yōu)化
class DataManager<T, K> {
constructor(private item1: T[], private item2: K[]) {}
getItem(index: number): string {
return `${this.item1[index]}${this.item2[index]}`;
}
}
const dataManager = new DataManager<string, number>(["1"], [2]);
interface Test {
name: string;
}
class Manager<T extends Test> {
constructor(private item: T[]) {}
getItem(index: number): string {
return this.item[index].name;
}
}
const manager = new Manager<Test>([{ name: "123" }]);
class Demo<T extends string | number>{
constructor(private item: T[]) {}
getItem(index: number): string|number {
return this.item[index];
}
}
const demo = new Demo(["123"]);
const demo1 = new Demo([123]);
泛型接口
interface IBaseUser<T> {
data: Array<T>;
add: (t: T) => T;
findUserById: (id: number) => T | undefined;
}
//用戶信息類
class User {
id?: number;
name: string;
age: number;
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
}
class UserCRUD implements IBaseUser<User> {
data: Array<User> = [];
add(user: User) {
if (!user.id) {
user.id = Date.now() + Math.random();
}
this.data.push(user);
return user;
}
findUserById(id: number) {
console.log("id:", id);
return this.data.find((item) => item.id == id);
}
}
const userCRUD = new UserCRUD();
userCRUD.add(new User("張三", 20));
const { id } = userCRUD.add(new User("李四", 21));
userCRUD.add(new User("王五", 22));
userCRUD.add(new User("趙六", 23));
console.log(userCRUD);
if (id) {
console.log(userCRUD.findUserById(id));
}
泛型約束:
如果我們直接對一個(gè)泛型參數(shù)取 length 屬性, 會(huì)報(bào)錯(cuò), 因?yàn)檫@個(gè)泛型根本就不知道它有這個(gè)屬性
// 沒有泛型約束
function fn <T>(x: T): void {
// console.log(x.length) // error
}
我們可以使用泛型約束來實(shí)現(xiàn)
interface Lengthwise {
length: number;
}
// 指定泛型約束
function fn2 <T extends Lengthwise>(x: T): void {
console.log(x.length)
}
我們需要傳入符合約束類型的值,必須包含必須 length 屬性:
fn2('abc')
// fn2(123) // error number沒有l(wèi)ength屬性
五:namespace:類似于模塊化的概念,減少全局變量的產(chǎn)生,把一組相關(guān)的內(nèi)容封裝在一起.
class Header {
constructor() {
const ele = document.createElement("div");
ele.innerText = "This is Header";
document.body.appendChild(ele);
}
}
class Content {
constructor() {
const ele = document.createElement("div");
ele.innerText = "This is Content";
document.body.appendChild(ele);
}
}
class Footer {
constructor() {
const ele = document.createElement("div");
ele.innerText = "This is Footer";
document.body.appendChild(ele);
}
}
class Page {
constructor() {
new Header();
new Content();
new Footer();
}
}
我們可以通過namespace來進(jìn)行優(yōu)化:通過namespace來創(chuàng)建一個(gè)命名空間,通過export來將需要用到的變量進(jìn)行導(dǎo)出.
namespace Home {
class Header {
constructor() {
const ele = document.createElement("div");
ele.innerText = "This is Header";
document.body.appendChild(ele);
}
}
class Content {
constructor() {
const ele = document.createElement("div");
ele.innerText = "This is Content";
document.body.appendChild(ele);
}
}
class Footer {
constructor() {
const ele = document.createElement("div");
ele.innerText = "This is Footer";
document.body.appendChild(ele);
}
}
export class Page {
constructor() {
new Header();
new Content();
new Footer();
}
}
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<script src="./dist/test.js"></script>
</head>
<body>
<script>
new Home.Page();
</script>
</body>
</html>
如果我們在多個(gè)地方引用Header Content Footer的話,再度優(yōu)化上面的代碼.
namespace Component {
export class Header {
constructor() {
const ele = document.createElement("div");
ele.innerText = "This is Header";
document.body.appendChild(ele);
}
}
export class Content {
constructor() {
const ele = document.createElement("div");
ele.innerText = "This is Content";
document.body.appendChild(ele);
}
}
export class Footer {
constructor() {
const ele = document.createElement("div");
ele.innerText = "This is Footer";
document.body.appendChild(ele);
}
}
}
///<reference path="./components.ts"/>
namespace Home {
export class Page {
constructor() {
new Component.Header();
new Component.Content();
new Component.Footer();
}
}
}
像上面這樣,我們可以通過引用components.ts對Component中的Header,Content,Footer進(jìn)行復(fù)用.
進(jìn)一步的優(yōu)化,通過模塊化來處理上面的問題, amd -- > import define
六. 類型定義文件:幫助Ts文件理解引入的Js文件/js庫里面的內(nèi)容.
因?yàn)镴s是中是TS中要求的類型的概念.(類型描述文件: .d.ts文件 )
以引入jquery為例:
//index.html
<!--
* @Author: itw_liuchao04 itw_liuchao04@tkmail.com
* @Date: 2022-10-26 13:54:30
* @LastEditors: itw_liuchao04 itw_liuchao04@tkmail.com
* @LastEditTime: 2022-10-26 13:57:52
* @FilePath: \Ts\src\index.html
* @Description:
*
* Copyright (c) 2022 by itw_liuchao04 itw_liuchao04@tkmail.com, All Rights Reserved.
-->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.1/jquery.js"></script>
<script src="./page.js"></script>
</head>
<body></body>
</html>
//page.ts
$(function () {
console.log("abc");
$("body").html("<div>123</div>");
});
//jquery.d.ts
//定義全局變量
//declare var $: (params: () => void) => void;
//定義全局方法
declare function $(params: () => void): () => void;
interface JqueryInstance {
html(param: string): () => void;
}
declare function $(seclotor: string): JqueryInstance;
//使用 interface語法,實(shí)現(xiàn)函數(shù)重載
interface JQuery {
(params: () => void): void;
(params: string): JqueryInstance;
}
declare var $: JQuery;
七:泛型中keyof語法的使用
interface Person {
name: string;
age: number;
gender: string;
}
class Teacher {
constructor(private info: Person) {}
getInfo(key: string) {
//類型保護(hù)
if (key === "name" || key === "age" || key === "gender") {
return this.info[key];
}
return "非法參數(shù)";
}
}
let person = {
name: "張三",
age: 18,
gender: "male",
};
const teacher = new Teacher(person);
console.log(teacher.getInfo("name"));//張三
console.log(teacher.getInfo("hobby"));//非法參數(shù)
使用keyof來替換上面的代碼實(shí)現(xiàn)
interface Person {
name: string;
age: number;
gender: string;
}
class Teacher {
constructor(private info: Person) {}
getInfo<T extends keyof Person>(key: T) {
//類型保護(hù)
// if (key === "name" || key === "age" || key === "gender") {
// return this.info[key];
// }
return this.info[key];
}
}
let person = {
name: "張三",
age: 18,
gender: "male",
};
const teacher = new Teacher(person);
console.log(teacher.getInfo("name")); //張三
console.log(teacher.getInfo("hobby")); //Argument of type '"hobby"' is not assignable to parameter of type 'keyof Person'