module
關鍵字既能夠稱作「內部模塊」,也能夠稱作「外部模塊」。這讓剛剛接觸 TypeScript 的開發者會有些困惑。以前javascript
module Math {
export function add(x, y) { ... }
}
複製代碼
以後css
namespace Math {
export function add(x, y) { ... }
}
複製代碼
// tsconfig.json
{
/* Strict Type-Checking Options */
"strict": true, /* Enable all strict type-checking options. */
// "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */
// 對 null 類型檢查,設置爲 false 就不會報錯了
// "strictNullChecks": true, /* Enable strict null checks. */
// "strictFunctionTypes": true, /* Enable strict checking of function types. */
// "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */
// "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */
// "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */
// "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */
}
複製代碼
strictNullChecks
參數用於新的嚴格空檢查模式,在嚴格空檢查模式下,null 和 undefined 值都不屬於任何一個類型,它們只能賦值給本身這種類型或者 anylet aaa: [string, number] = ['aaa', 5];
// 添加時不會報錯
aaa.push(6);
// 打印整個元祖不會報錯
console.log(aaa); // ['aaa',5,6];
// 打印添加的元素時會報錯
console.log(aaa[2]); // error
複製代碼
enum Gender {
BOY = 1,
GRIL
}
console.log(Gender.BOY);// 1
console.log(Gender);// { '1': 'BOY', '2': 'GRIL', BOY: 1, GRIL: 2 }
複製代碼
enum Char {
// const member 常量成員:在編譯階段被計算出結果
a, // 沒有初始值
b = Char.a,// 對常量成員的引用
c = 1 + 3, // 常量表達式
// computed member 計算成員:表達式保留到程序的執行階段
d = Math.random(),// 很是量表達式
e = '123'.length,
// 緊跟在計算成員後面的枚舉成員必須有初始值
f = 6,
g
}
複製代碼
const enum Colors {
Red,
Yellow,
Blue
}
// 常量枚舉會在編譯階段被刪除
let myColors = [Colors.Red, Colors.Yellow, Colors.Blue];
複製代碼
編譯成 JShtml
"use strict";
var myColors = [0 /* Red */, 1 /* Yellow */, 2 /* Blue */];
複製代碼
// 報錯
const enum Color {Red, Yellow, Blue = "blue".length};
console.log(Colors.RED);
複製代碼
如下代碼存在的問題:java
function initByRole(role) {
if (role === 1 || role == 2) {
console.log("1,2")
} else if (role == 3 || role == 4) {
console.log('3,4')
} else if (role === 5) {
console.log('5')
} else {
console.log('')
}
}
複製代碼
使用枚舉後node
enum Role {
Reporter,
Developer,
Maintainer,
Owner,
Guest
}
function init(role: number) {
switch (role) {
case Role.Reporter:
console.log("Reporter:1");
break;
case Role.Developer:
console.log("Developer:2");
break;
case Role.Maintainer:
console.log("Maintainer:3");
break;
case Role.Owner:
console.log("Owner:4");
break;
default:
console.log("Guest:5");
break;
}
}
init(Role.Developer);
複製代碼
// 數字索引——約束數組
// index 是隨便取的名字,能夠任意取名
// 只要 index 的類型是 number,那麼值的類型必須是 string
interface StringArray {
// key 的類型爲 number ,通常都表明是數組
// 限制 value 的類型爲 string
[index:number]:string
}
let arr:StringArray = ['aaa','bbb'];
console.log(arr);
// 字符串索引——約束對象
// 只要 index 的類型是 string,那麼值的類型必須是 string
interface StringObject {
// key 的類型爲 string ,通常都表明是對象
// 限制 value 的類型爲 string
[index:string]:string
}
let obj:StringObject = {name:'ccc'};
複製代碼
// 注意區別
// 普通的接口
interface discount1{
getNum : (price:number) => number
}
// 函數類型接口
interface discount2{
// 注意:
// 「:」 前面的是函數的簽名,用來約束函數的參數
// ":" 後面的用來約束函數的返回值
(price:number):number
}
let cost:discount2 = function(price:number):number{
return price * .8;
}
// 也可使用類型別名
type Add = (x: number, y: number) => number
let add: Add = (a: number, b: number) => a + b
複製代碼
// 接口能夠在面向對象編程中表示爲行爲的抽象
interface Speakable {
name: string;
// ":" 前面的是函數簽名,用來約束函數的參數
// ":" 後面的用來約束函數的返回值
speak(words: string): void
}
interface Speakable2 {
age: number;
}
class Dog implements Speakable, Speakable2 {
name!: string;
age = 18;
speak(words: string) {
console.log(words);
}
}
let dog = new Dog();
dog.speak('汪汪汪');
複製代碼
interface FnType {
(getName:string):string;
}
interface MixedType extends FnType{
name:string;
age:number;
}
複製代碼
interface Counter {
(start: number): string;
interval: number;
reset(): void;
}
function getCounter(): Counter {
let counter = <Counter>function (start: number) { };
counter.interval = 123;
counter.reset = function () { };
return counter;
}
let c = getCounter();
c(10);
c.reset();
c.interval = 5.0;
複製代碼
function attr(val: string): string;
function attr(val: number): number;
// 前面兩行是函數申明,這一行是實現函數重載
function attr(val: any): any {
if (typeof val === 'string') {
return val;
} else if (typeof val === 'number') {
return val;
}
}
attr('aaa');
attr(666);
複製代碼
// 後寫的接口中的函數聲明優先級高
interface Cloner111 {
clone(animal: Animal): Animal;
}
interface Cloner111 {
clone(animal: Sheep): Sheep;
}
interface Cloner111 {
clone(animal: Dog): Dog;
clone(animal: Cat): Cat;
}
// ==> 同名接口會合並
// 後寫的接口中的函數聲明優先級高
interface Cloner111 {
clone(animal: Dog): Dog;
clone(animal: Cat): Cat;
clone(animal: Sheep): Sheep;
clone(animal: Animal): Animal;
}
interface Cloner222 {
// 接口內部按書寫的順序來排,先寫的優先級高
clone(animal: Dog): Dog;
clone(animal: Cat): Cat;
clone(animal: Sheep): Sheep;
clone(animal: Animal): Animal;
}
複製代碼
class Father {
str: string; // 默認就是 public
public name: string; // 在定義的類中、類的實例、子類、子類實例均可以訪問
protected age: number; // 只能在定義的類和子類中訪問,不容許經過實例(定義的類的實例和子類實例)訪問
private money: number; // 只能在定義的類中訪問,類的實例、子類、子類實例都不能夠訪問
constructor(name: string, age: number, money: number) {
this.name = name;
this.age = age;
this.money = money;
}
getName(): string {
return this.name;
}
setName(name: string): void {
this.name = name;
}
}
const fa = new Father('aaa', 18, 1000);
console.log(fa.name);// aaa
console.log(fa.age);// error
console.log(fa.money);// error
class Child extends Father {
constructor(name: string, age: number, money: number) {
super(name, age, money);
}
desc() {
console.log(`${this.name} ${this.age} ${this.money}`);
}
}
let child = new Child('bbb', 18, 1000);
console.log(child.name);// bbb
console.log(child.age);// error
console.log(child.money);// error
複製代碼
class Animal {
speak(word: string): string {
return '動做叫:' + word;
}
}
class Cat extends Animal {
speak(word: string): string {
return '貓叫:' + word;
}
}
let cat = new Cat();
console.log(cat.speak('hello'));
/**--------------------------------------------**/
function double(val: number): number
function double(val: string): string
function double(val: any): any {
if (typeof val == 'number') {
return val * 2;
}
return val + val;
}
let r = double(1);
console.log(r);
複製代碼
class Animal {
speak(word: string): string {
return 'Animal: ' + word;
}
}
class Cat extends Animal {
speak(word: string): string {
return 'Cat:' + word;
}
}
class Dog extends Animal {
speak(word: string): string {
return 'Dog:' + word;
}
}
let cat = new Cat();
console.log(cat.speak('hello'));
let dog = new Dog();
console.log(dog.speak('hello'));
複製代碼
// 咱們但願傳入的值是什麼類型,返回的值就是什麼類型
// 傳入的值能夠是任意的類型,這時候就能夠用到 泛型
// 若是使用 any 的話,就失去了類型檢查的意義
function createArray1(length: any, value: any): Array<any> {
let result: any = [];
for (let i = 0; i < length; i++) {
result[i] = value;
}
return result;
}
let result = createArray1(3, 'x');
console.log(result);
// 最傻的寫法:每種類型都得定義一種函數
function createArray2(length: number, value: string): Array<string> {
let result: Array<string> = [];
for (let i = 0; i < length; i++) {
result[i] = value;
}
return result;
}
function createArray3(length: number, value: number): Array<number> {
let result: Array<number> = [];
for (let i = 0; i < length; i++) {
result[i] = value;
}
return result;
}
// 或者使用函數重載,寫法有點麻煩
function createArray4(length: number, value: number): Array<number> function createArray4(length: number, value: string): Array<string> function createArray4(length: number, value: any): Array<any> { let result: Array<number> = []; for (let i = 0; i < length; i++) {
result[i] = value;
}
return result;
}
createArray4(6, '666');
複製代碼
使用泛型react
// 有關聯的地方都改爲 <T>
function createArray<T>(length: number, value: T): Array<T> {
let result: T[] = [];
for (let i = 0; i < length; i++) {
result[i] = value;
}
return result;
}
// 使用的時候再指定類型
let result = createArray<string>(3, 'x');
// 也能夠不指定類型,TS 會自動類型推導
let result2 = createArray(3, 'x');
console.log(result);
複製代碼
parameterName is Type
這種形式,其中 parameterName
必須是當前函數簽名裏的一個參數名interface Bird {
fly()
layEggs()
}
interface Fish {
swim()
layEggs()
}
function getSmallPet():Fish | Bird{
return ;
}
let pet = getSmallPet();
pet.layEggs();
// 當使用聯合類型時,若是不用類型斷言,默認只會從中獲取共有的部分
(pet as Fish).swim();
pet.swim();
複製代碼
interface Bird {
fly()
layEggs()
}
interface Fish {
swim()
layEggs()
}
function getSmallPet():Fish | Bird{
return ;
}
let pet = getSmallPet();
// 使用類型謂詞
function isFish(pet:Fish | Bird):pet is Fish {
return (pet as Fish).swim !== undefined;
}
if(isFish(pet)){
pet.swim();
}else{
pet.fly();
}
複製代碼
?.
?.
計算爲 undefined 或 null,則表達式求值爲 undefined 。不然,正常觸發目標屬性訪問、方法或函數調用。a?.b;
// 至關於 a == null ? undefined : a.b;
// 若是 a 是 null/undefined,那麼返回 undefined,不然返回 a.b 的值.
a?.[x];
// 至關於 a == null ? undefined : a[x];
// 若是 a 是 null/undefined,那麼返回 undefined,不然返回 a[x] 的值
a?.b();
// 至關於a == null ? undefined : a.b();
// 若是 a 是 null/undefined,那麼返回 undefined
// 若是 a.b 不是函數的話,會拋類型錯誤異常,不然計算 a.b() 的結果
複製代碼
let root: any = document.getElementById('root');
root.style.color = 'red';
let root2: (HTMLElement | null) = document.getElementById('root');
// 非空斷言操做符--> 這樣寫只是爲了騙過編譯器,防止編譯的時候報錯,打包後的代碼可能仍是會報錯
root2!.style.color = 'red';
複製代碼
||
運算符的缺點: 當左側表達式的結果是數字 0 或空字符串時,會被視爲 false
。null
或 undefined
時,纔會返回右側表達式的結果。經過這種方式能夠明確地區分 undefined、null
與 false
的值。const data = {
str:'',
// num:0,
flag:false,
// flag: null,
};
// data.str 爲 "" 時
let str1 = data.str || '空' // '空'
// data.num 爲 0 時
let num1 = data.num || 666 // 666
// data.flag 爲 false 時
let status1 = data.flag || true // true
// data.str 爲 "" 時,能夠經過。僅在 str 爲 undefined 或者 null 時,不能夠經過
let st2r = data.str ?? '空';
// data.num 爲 0 時,能夠經過。僅在 num 爲 undefined 或者 null 時,不能夠經過
let num2 = data.num ?? 666;
// data.flag 爲 false 時,能夠經過。僅在 flag 爲 undefined 或者 null 時,不能夠經過
let status2 = data.flag ?? true;
console.log('str=>', str2);
console.log('num=>', num2);
console.log('status=>', status2);
複製代碼
class Greeter {
static message = 'hello';
greet(){
return Greeter.message;
}
}
// 獲取的是實例的類型,該類型能夠獲取實例對象上的屬性/方法
let greeter1:Greeter = new Greeter();
console.log(greeter1.greet());// 'hello'
// 獲取的是類的類型,該類型能夠獲取類上面的靜態屬性/方法
let greeterTwo:typeof Greeter = Greeter;
greeterTwo.message = 'hey';
let greeter2:Greeter = new greeterTwo();
console.log(greeter2.greet());// 'hey'
複製代碼
interface Bird {
fly()
layEggs()
}
interface Fish {
swim()
layEggs()
}
function getSmallPet():Fish | Bird{
return ;
}
let pet = getSmallPet();
pet.layEggs();
// 當使用聯合類型時,在類型未肯定的狀況下,默認只會從中獲取共有的部分
// 須要使用類型斷言
(pet as Fish).swim();
pet.swim();
複製代碼
enum KindType{
square = 'square',
rectangle = 'rectangle',
circle = 'circle',
}
interface Square {
kind: KindType.square;
size: number;
}
interface Rectangle {
kind: KindType.rectangle;
width: number;
height: number;
}
interface Circle {
kind: KindType.circle;
radius: number;
}
type Shape = Square | Rectangle | Circle;
function area1(s: Shape) {
// 若是聯合類型中的多個類型,擁有共有的屬性,那麼就能夠憑藉這個屬性來建立不一樣的類型保護區塊
// 這裏 kind 是共有的屬性
switch (s.kind) {
case KindType.square:
return s.size * s.size;
case KindType.rectangle:
return s.height * s.width;
default:
return;
}
}
// 以上代碼有隱患,若是後續新增類型時,TS 檢查以上代碼時,雖然缺失後續新增的類型,但不會報錯
console.log(area1({kind: KindType.circle, radius: 1}));
function area2(s: Shape) {
switch (s.kind) {
case KindType.square:
return s.size * s.size;
case KindType.rectangle:
return s.height * s.width;
case KindType.circle:
return Math.PI * s.radius ** 2;
default:
// 檢查 s 是不是 never 類型
// 若是是 never 類型,那麼上面的分支語句都被覆蓋了,就永遠都不會走到當前分支
// 若是不是 never 類型。就說明前面的分支語句有遺漏,須要補上
return ((e: never) => { throw new Error(e) })(s) } } console.log(area2({kind: KindType.circle, radius: 1})); 複製代碼
let name: string;
// 加了 export 後就不會報錯
// export {}
複製代碼
你可能會寫出下面這樣的代碼:將命名空間導出webpack
shapes.ts
export namespace Shapes {
export class Triangle { /* ... */ }
export class Square { /* ... */ }
}
複製代碼
shapeConsumer.ts
import * as shapes from "./shapes";
let t = new shapes.Shapes.Triangle();
複製代碼
不該該在模塊中使用命名空間或者說將命名空間導出: 使用命名空間是爲了提供邏輯分組和避免命名衝突,模塊文件自己已是一個邏輯分組,而且它的名字是由導入這個模塊的代碼指定,因此沒有必要爲導出的對象增長額外的模塊層。git
下面是改進的例子:es6
shapes.ts
export class Triangle { /* ... */ }
export class Square { /* ... */ }
複製代碼
shapeConsumer.ts
import * as shapes from "./shapes";
let t = new shapes.Triangle();
複製代碼
或者github
shapes.ts
namespace Shapes {
export class Triangle { /* ... */ }
export class Square { /* ... */ }
}
複製代碼
shapeConsumer.ts
let t = new Shapes.Triangle();
複製代碼
interface String {
// 這裏是擴展,不是覆蓋,因此放心使用
double(): string;
}
String.prototype.double = function () {
return this + '+' + this;
};
console.log('hello'.double());
// 若是加了這個,就會報錯
// export {}
複製代碼
interface Window {
myname: string
}
// 注意:這裏的 window 要小寫
console.log(window);
// 若是加了這個,當前模塊就會變成局部的
// 而後定義的類型 Window 就是局部的變量,再也不是一個全局變量
// 因此上面給 Window 擴展屬性/方法就失效了
export {}
複製代碼
// exports === module.exports // 即:這兩個變量共用一個內存地址
// 總體導出
// module.exports = {}
// 導出多個變量
exports.c = 3;
exports.d = 4;
複製代碼
// 兼容性寫法只在 TS 中有效 !!!!!!
// 兼容性寫法只在 TS 中有效 !!!!!!
// 兼容性寫法只在 TS 中有效 !!!!!!
// a.es6.ts
// 這裏只能導出一個
export = function () {
console.log("I'm default")
}
// b.node.ts
import fn = require('./a.es6.ts');
fn();
複製代碼
.tsx
文件裏禁用了使用尖括號的類型斷言。as
操做符在 .ts
文件和 .tsx
文件裏均可用interface Person {
name: string;
age: number
}
let p1 = {age: 18} as Person;
console.log(p1.name);
// 這種寫法在 .tsx 文件中會報錯
let p2 = <Person>{age: 18};
console.log(p2.name);
複製代碼
checkJs:true
,對 .js
文件進行類型檢查和錯誤提示。
.js
文件頂部添加 // @ts-nocheck
註釋,讓編譯器忽略當前文件的類型檢查。checkJs:true
並在 .js
文件頂部添加一個 // @ts-check
註釋,讓編譯器檢查當前文件。// @ts-ignore
來忽略本行的錯誤。.js
文件裏,類型能夠和在 .ts
文件裏同樣被推斷出來。當類型不能被推斷時,能夠經過 JSDoc 來指定類型。/** @type {number} */
var x;
x = 0; // OK
x = false; // Error: boolean is not assignable to number
複製代碼
/* 錯誤 */
function reverse(s: String): String;
/* OK */
function reverse(s: string): string;
複製代碼
function fn({ x: number }) { /* ... */ }
時,即能給變量聲明類型,又能給變量設置默認值// error
function f({ x: number }) {
console.log(x);
}
// ok
function f({x}: { x: number } = {x: 0}) {
console.log(x);
}
複製代碼
interface Test {
arr: string[]
}
// pick 摘取返回的結果 => {arr: string[]}
let aaa: Pick<Test, 'arr'> = {arr: ['1']};
複製代碼
const map = new Map([
['F', 'no'],
['T', 'yes'],
]);
for (let key of map.keys()) {
console.log(key);
}
// 用 forEach 也能夠遍歷
map.forEach((value,key) => {
console.log(key);
});
複製代碼
TS2569: Type 'Map<string, string>' is not an array type or a string type. Use compiler. option '- downlevellteration' to allow iterating of iterators.
配置 dom.iterable 和 downlevelIteration 就能夠正常運行
tsconfig.json
{
/*當目標是ES5或ES3的時候提供對for-of、擴展運算符和解構賦值中對於迭代器的完整支持*/
"downlevelIteration": true,
"lib": [
"dom",
"es5",
"es6",
"es7",
"dom.iterable"
]
}
複製代碼
注意:若是未指定--lib,則會注入默認的庫列表。注入的默認庫是:
► For --target ES5: DOM,ES5,ScriptHost
► For --target ES6: DOM,ES6,DOM.Iterable,ScriptHost
interface User {
username: string
id: number
token: string
avatar: string
role: string
}
type UserWithoutToken = Omit<User, 'token'>
複製代碼
有時候是被 tsconfig.json 自動加入的,若是編譯器識別出一個文件是模塊導入目標,它就會加到編譯列表裏,無論它是否被排除了。所以,要從編譯列表中排除一個文件,你須要在排除它的同時,還要排除全部對它進行 import 或使用了 /// 指令的文件。
// a.ts
namespace Shape {
const pi = Math.PI;
export function cricle(r: number) {
return pi * r ** 2
}
}
// b.ts
// 直接使用
// console.log(Shape.cricle(2));
// 或者經過如下方式來使用該命名空間中的變量/函數/類
// import newName = a.b.c.d 用來給經常使用的、層級較深的對象起一個短的名字
// 這裏的 import 的做用是建立一個別名,爲任意標識符建立別名,包括導入的模塊中的對象
// 不要與用來加載模塊的 import x from "module-name" 語法弄混了
import cricle = Shape.cricle;
console.log(cricle(2));
複製代碼
require
關鍵字,而是直接使用導入符號的限定名賦值。 這與使用 var
類似,但它還適用於類型和導入的具備命名空間含義的符號。 重要的是,對於值來說,import
會生成與原始符號不一樣的引用,因此改變別名的 var
值並不會影響原始變量的值。{
"compilerOptions": {
/**************基礎配置**************/
/**************基礎配置**************/
/**************基礎配置**************/
/* 開啓增量編譯:TS 編譯器在第一次編譯的時候,會生成一個存儲編譯信息的文件,下一次編譯的時候,會根據這個文件進行增量的編譯,以此提升 TS 的編譯速度 */
// "incremental": true,
/* 指定存儲增量編譯信息的文件位置 */
// "tsBuildInfoFile": "./",
/* 打印診斷信息 */
// "diagnostics": true,
/* 打印輸出的文件 */
// "listEmittedFiles": true,
/* 打印編譯的文件(包括引用的聲明文件)*/
// "listFiles": true,
/* 指定 ECMAScript 的目標版本: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */
// "target": "es5",
/* 指定模塊代碼的生成方式: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */
// "module": "commonjs",
/* 指定要包含在編譯中的庫文件——引用類庫——即申明文件,若是輸出的模塊方式是 es5,就會默認引入 "dom","es5","scripthost" */
/* 若是在 TS 中想要使用一些 ES6 以上版本的語法,就須要引入相關的類庫 */
// "lib": [],
/* 容許編譯 JS 文件 */
// "allowJs": true,
/* 檢查 JS 文件*/
// "checkJs": true,
/* 指定 JSX 代碼生成的模式: 'preserve', 'react-native', or 'react'. */
/* 'react' 模式下:TS 會直接把 jsx 編譯成 js */
/* 'preserve' 模式下:TS 不會把 jsx 編譯成 js,會保留 jsx */
// "jsx": "preserve",
/**************聲明文件相關配置**************/
/**************聲明文件相關配置**************/
/**************聲明文件相關配置**************/
/* 生成相應的類型聲明文件 —— '.d.ts' */
// "declaration": true,
/* 聲明文件的輸出路徑 */
// "declarationDir": "./d",
/* 只生成聲明文件,不生成 JS */
// "emitDeclarationOnly": true,
/* 聲明文件目錄,默認 node_modules/@types */
// "typeRoots": [],
/* 要導入的聲明文件包,默認導入上面聲明文件目錄下的全部聲明文件 */
// "types": [],
/* 將多個相互依賴的文件合併而且把編譯後的內容輸出到一個文件裏 * 能夠用在產出 AMD 模塊的場景中 * "module":"amd" 時,當一個模塊引入了另一個模塊,編譯的時候會把這兩個模塊的編譯結果合併到一個文件中 */
// "outFile": "./",
/* 指定編譯文件的輸出目錄 */
// "outDir": "./out",
/* 指定輸入文件的根目錄,用於控制輸出目錄的結構 */
// "rootDir": "./",
/* 啓用項目編譯 */
// "composite": true,
/* 輸出的時候移除註釋 */
// "removeComments": true,
/* 不輸出文件 */
// "noEmit": true,
/* 發生錯誤時不輸出文件 */
// "noEmitOnError": true,
/* 不生成 helper 函數,之前的話設置爲 true 後,須要額外安裝 ts-helpers */
/* 相似於 babel ,會給每一個文件都生成 helper 函數,會使得最終編譯後的包的體積變大 */
// "noEmitHelpers": true,
/* 如今能夠經過 tslib(TS 內置的庫)引入 helper 函數,!!!文件必須是模塊 !!! */
/* 編譯後自動引入 var tslib_1 = require("tslib") */
// "importHelpers": true,
/* 當目標是 ES5 或 ES3 的時候提供對 for-of、擴展運算符和解構賦值中對於迭代器的完整支持 */
// "downlevelIteration": true,
/* 把每個文件轉譯成一個單獨的模塊 */
// "isolatedModules": true,
/**************嚴格檢查配置**************/
/**************嚴格檢查配置**************/
/**************嚴格檢查配置**************/
/* 開啓全部的嚴格檢查配置 */
"strict": true,
/* 不容許使用隱式的 any 類型 */
// "noImplicitAny": true,
/* 不容許把 null、undefined 賦值給其餘類型變量 */
// "strictNullChecks": true,
/* 不容許函數參數雙向協變 */
// "strictFunctionTypes": true,
/* 使用 bind/call/apply 時,嚴格檢查函數參數類型 */
// "strictBindCallApply": true,
/* 類的實例屬性必須初始化 */
// "strictPropertyInitialization": true,
/* 不容許 this 有隱式的 any 類型,即 this 必須有明確的指向*/
// "noImplicitThis": true,
/* 在嚴格模式下解析而且向每一個源文件中注入 "use strict" */
// "alwaysStrict": true,
/**************額外的語法檢查配置,這種檢查交給 eslint 就行,不必配置**************/
/**************額外的語法檢查配置,這種檢查交給 eslint 就行,不必配置**************/
/**************額外的語法檢查配置,這種檢查交給 eslint 就行,不必配置**************/
/* 有未使用到的本地變量時報錯 */
// "noUnusedLocals": true,
/* 有未使用到的函數參數時報錯 */
// "noUnusedParameters": true,
/* 每一個分支都要有返回值 */
// "noImplicitReturns": true,
/* 嚴格校驗 switch-case 語法 */
// "noFallthroughCasesInSwitch": true,
/**************模塊解析配置**************/
/**************模塊解析配置**************/
/**************模塊解析配置**************/
/* 指定模塊的解析策略: 'node' (Node.js) or 'classic' (TypeScript pre-1.6)*/
/* 若未指定,那麼在使用了 --module AMD | System | ES2015 時的默認值爲 Classic,其它狀況時則爲 Node */
// "moduleResolution": "node",
/* 在解析非絕對路徑模塊名的時候的基準路徑 */
// "baseUrl": "./",
/* 基於 'baseUrl' 的路徑映射集合 */
// "paths": {},
/* 將多個目錄放在一個虛擬目錄下,用於運行時 */
/* 當本身編寫的庫和開發的代碼都輸出到一個目錄下時,開發代碼和庫的位置不同,開發代碼引入庫的路徑就會不對 */
// "rootDirs": [],
// "rootDirs": ["src","out"],
/* 容許 export = xxx 導出 ,並使用 import xxx form "module-name" 導入*/
// "esModuleInterop": true,
/* 當模塊沒有默認導出的時候,容許被別的模塊默認導入,這個在代碼執行的時候沒有做用,只是在類型檢查的時候生效 */
// "allowSyntheticDefaultImports": true,
/* 不要 symlinks 解析的真正路徑 */
// "preserveSymlinks": true,
/* 容許在模塊中以全局變量的方式訪問 UMD 模塊內容 */
// "allowUmdGlobalAccess": true,
/************** Source Map 配置**************/
/************** Source Map 配置**************/
/************** Source Map 配置**************/
/* 指定 ts 文件位置 */
// "sourceRoot": "",
/* 指定 map 文件存放的位置 */
// "mapRoot": "",
/* 生成目標文件的 sourceMap */
// "sourceMap": true,
/* 將代碼與sourcemaps生成到一個文件中,要求同時設置了--inlineSourceMap 或--sourceMap 屬性*/
// "inlineSources": true,
/* 生成目標文件的 inline sourceMap —— 源文件和 sourcemap 文件在同一文件中,而不是把 map 文件放在一個單獨的文件裏*/
// "inlineSourceMap": true,
/* 生成聲明文件的 sourceMap */
// "declarationMap": true,
/************** 實驗性的配置**************/
/************** 實驗性的配置**************/
/************** 實驗性的配置**************/
/* 啓用裝飾器 */
// "experimentalDecorators": true,
// "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */
/**************高級配置**************/
/**************高級配置**************/
/**************高級配置**************/
/* 強制區分大小寫 */
// "forceConsistentCasingInFileNames": true
}
/* 指定須要編譯的單個文件列表 */
// "files": [],
/* 指定須要編譯的文件/目錄 */
// "include": [
// // 只寫一個目錄名等價於 "./src/**/*"
// "src"
// ]
/* 須要排除的文件或目錄 */
// "exclude": []
/* 配置文件繼承 */
// "extends": "./tsconfig.base.json"
}
複製代碼
模式 | 輸入 | 輸出 | 輸出文件擴展名 |
---|---|---|---|
preserve | <div /> |
<div /> |
.jsx |
react | <div /> |
React.createElement("div") |
.js |
react-native | <div /> |
<div /> |
.js |
TypeScript
時,會順帶安裝 lib.d.ts
等聲明文件,此文件包含了 JavaScript 運行時以及 DOM 中存在各類常見的環境聲明。
tsconfig.json
中的 lib 選項用來指定當前項目須要注入哪些聲明庫文件。若是沒有指定,默認注入的庫文件列表爲:
--target ES5
:DOM,ES5,ScriptHost
--target ES6
:DOM,ES6,DOM.Iterable,ScriptHost
ES7
、 DOM.Iterable
import * as abc from "@babel/types"
這種非相對路徑的導入,不能正確解析。// 配置前
import * as React from 'react';
import * as ReactDOM from 'react-dom';
// 配置後
import React from 'react';
import ReactDOM from 'react-dom';
複製代碼
{
"paths": {
// 這裏的路徑後面必須跟着 "/*"
"@public/*": [
// 這裏的路徑後面必須跟着 "/*"
"public/*"
],
"@src/*": [
"src/*"
],
"@assets/*":[
"src/assets/*"
],
"@components/*": [
"src/components/*"
]
}
}
複製代碼
import styles from './login.less';
import logo from '@assets/images/logo.svg';
const logo2 = require('@assets/images/logo.svg');
console.log(logo2);// path
複製代碼
解決辦法: 給這些非 JS 模塊添加申明
/** * style */
declare module '*.css'
declare module '*.less'
// declare module "*.less" {
// const styles: { [className: string]: string };
// export default styles
// }
declare module '*.scss'
/**
* 圖片
*/
declare module '*.svg'
declare module '*.png'
declare module '*.jpg'
declare module '*.jpeg'
declare module '*.gif'
declare module '*.bmp'
複製代碼
{
// 容許 默認導入 沒有設置默認導出(export default xxx)的模塊
// 能夠以 import xxx from 'xxx' 的形式來引入模塊
"allowSyntheticDefaultImports":true
}
複製代碼
// 配置前
import * as React from 'react';
import * as ReactDOM from 'react-dom';
// 配置後
import React from 'react';
import ReactDOM from 'react-dom';
複製代碼
.babelrc
{
"presets": [
"@babel/preset-react",
"@babel/preset-env"
],
"plugins": [
[
"import",
{
"libraryName": "antd",
"libraryDirectory": "es",
"style": "css"
/* `style: true` 會加載 less 文件*/
}
]
]
}
複製代碼
tsconfig.json
{
"compilerOptions": {
"target": "es5",
"jsx": "preserve",// 保留 jsx
...
}
複製代碼
webpack.config.js
{
test: /\.tsx?$/,
use: [
'babel-loader',
'ts-loader'
]
},
複製代碼
// 源碼
// interface RefObject<T> {
// readonly current: T | null;
// }
const ref1:React.RefObject<HTMLDivElement> = React.createRef();
const inputRef = React.createRef<Comp>();
class EditScene extends React.Component<Props> {
inputRef:React.RefObject<Comp>
constructor(props) {
super(props);
this.inputRef = React.createRef<Comp>();
}
}
複製代碼
import {ComponentClass} from 'react'
import {
connect as nativeConnect,
MapDispatchToPropsParam,
MapStateToPropsParam
} from 'react-redux'
import {withRouter as nativeWithRouter} from 'react-router-dom'
export type ComponentDecorator<P = any> = <T extends ComponentClass<P>>(WrappedComponent: T) => T
export const connect: <P, S>(
mapState: MapStateToPropsParam<Partial<P>, P, S>,
// mapDispatch?: MapDispatchToPropsParam<Partial<P>, P>
mapDispatch?: any
) => ComponentDecorator = nativeConnect as any;
export const withRouter: ComponentDecorator = nativeWithRouter as any;
複製代碼
// rootReducer.ts
import {combineReducers} from 'redux';
import {connectRouter} from 'connected-react-router';
import history from '../history';
import evidenceEdit from './evidence';
import common from './common';
import work from './work';
import setScene from './set-scene';
let reducers = {
common,
work,
setScene,
evidenceEdit,
router: connectRouter(history)
};
// 使用 ReturnType 從 rootReducer 推斷狀態形狀
// export type AppState = ReturnType<typeof rootReducer>
export type AppState = {
[key in keyof typeof reducers]: ReturnType<typeof reducers[key]>
}
const rootReducer = combineReducers(reducers);
export default rootReducer;
複製代碼
// setScene 模塊
import * as types from '../types/action-types';
import {appEditAction} from '../actions/common';
export interface SetSceneState {
loadSuccess: boolean;
loadProgress: number;
}
let initState: SetSceneState = {
loadSuccess: false,
loadProgress: 0,
};
export default function (state: SetSceneState = initState, action: appEditAction) {
switch (action.type) {
case types.SCENE_DATA_LOADSUCCESS: {
return {...state, loadSuccess: action.payload.success};
}
case types.SCENE_DATA_LOADINGPROGRESS: {
return {...state, loadProgress: action.payload.num};
}
default:
return state;
}
}
複製代碼
使用
// 在 Mesh 組件中
import workActions from "@store/actions/work";
interface MeshProps {
// 剛開始我是這樣寫的,每次都得在組件的 Props 裏從新聲明一下函數
// updateSceneData?: (workId: string,data) => appEditAction;
updateData?: typeof workActions.updateData;
}
@connect(null, {
updateData: workActions.updateData,
})
class Mesh extends React.Component<MeshProps> {...}
複製代碼
// store/actions/work.ts
import * as types from '../types/action-types';
import {appEditAction} from "@edit-store/actions/common";
export default {
updateWorkData(workId: string, data: any): appEditAction {
return {type: types.UPDATE_WORK_ASYNC, payload: {workId, data}}
}
}
複製代碼
import * as React from 'react';
import {RouteComponentProps} from 'react-router';
import {connect} from "@store/connect";
import {AppState} from "@store/reducers";
import commonActions from "@store/actions/commonActions";
// 組件可能有四個屬性來源
// 1.mapStateToProps 的返回值
// 2.actions 對象類型
// 3.來自路由
// 4.父組件傳進來的其它屬性
// 原先的寫法:一個個拼起來,mapStateToProps 返回的狀態還得在 Props 接口裏再聲明一遍,比較混亂、麻煩
// interface Props {
// loadProgress?: number;
// markVisible?: boolean;
// setMarkVisible?: typeof commonActions.setMarkVisible;
// }
function mapStateToProps(state: AppState) {
const {markVisible,loadProgress} = state;
return {
markVisible,
loadProgress,
};
}
// 如今的寫法:便捷
type StateProps = ReturnType<typeof mapStateToProps>;
type DispatchProps = typeof commonActions;
interface IParams {}
type RouteProps = RouteComponentProps<IParams>;
type Props = StateProps & RouteProps & DispatchProps & {};
@connect(mapStateToProps, {
setMarkVisible: commonActions.setMarkVisible
})
export default class App extends React.PureComponent<Props, any> {
render() {
const {markVisible, loadProgress} = this.props;
return (<div > {markVisible} {loadProgress} </div>);
}
}
複製代碼
redux thunk 有一個內置類型 ThunkAction
,咱們能夠這樣使用:
// src/thunks.ts
import { Action } from 'redux'
import { sendMessage } from './store/chat/actions'
import { AppState } from './store'
import { ThunkAction } from 'redux-thunk'
export const thunkSendMessage = (
message: string
): ThunkAction<void, AppState, null, Action<string>> => async dispatch => {
const asyncResp = await exampleAPI()
dispatch(
sendMessage({
message,
user: asyncResp,
timestamp: new Date().getTime()
})
)
}
function exampleAPI() {
return Promise.resolve('Async')
}
複製代碼
# 下載這個類型聲明文件
$ npm install --save @types/webpack-env
複製代碼
if (process.env.NODE_ENV !== 'production') {
if (module.hot) {
module.hot.accept('./reducers', () => store.replaceReducer(rootReducer));
}
}
複製代碼
interface Greeting {
name: string;
age: number;
}
const Hello:React.FC<Greeting> = (props) => <h1>Hello {props.name}</h1>;
// 推薦使用第二種
const Hello2 = (props:Greeting) => <h1>Hello {props.name}</h1>;
複製代碼
import React, { Component } from 'react';
import HelloClass from './HelloClass';
interface Loading {
loading: boolean
}
// HOC 能夠接收一個類組件,也能夠接收一個函數組件,因此參數的類型是 React.ComponentType
// 源碼:type ComponentType<P = {}> = ComponentClass<P> | FunctionComponent<P>;
function HelloHOC<P>(WrappedComponent: React.ComponentType<P>) {
return class extends Component<P & Loading> {
render() {
const { loading, ...props } = this.props;
return loading ? <div>Loading...</div> : <WrappedComponent { ...props as P } />;
}
}
}
export default HelloHOC(HelloClass);
複製代碼
class Login extends React.Component <Props>{
handlerLinkBtnClick = (ev) => {
console.log(ev);
this.props.historyGo('./register');
};
handlerLinkBtnMouseMove = (ev) => {
console.log(ev);
};
render() {
return (
<div>
<header>
<p >This is Login Page </p>
<div className={styles.linkBtn}
onMouseMove={this.handlerLinkBtnMouseMove}
onClick={this.handlerLinkBtnClick}>
Go to Register Page
</div>
</header>
</div>
);
}
}
複製代碼
按住 Ctrl ,而後鼠標移動到事件名上就能獲取當前事件處理函數的參數類型