TypeScript 是 JS 的超集,強(qiáng)調(diào)變量類(lèi)型片部。讓JS更加接近強(qiáng)類(lèi)型語(yǔ)言,下圖說(shuō)明了接入 TS 的好處:
1、接入流程
- 安裝 tslint 和 typescript
npm i typescript -D
npm install typescript-eslint-parser --save-dev
npm install eslint-plugin-typescript --save-dev // typescript-eslint-parser 對(duì)一部分 ESLint 規(guī)則支持性不好财骨,故我們需要安裝 eslint-plugin-typescript,彌補(bǔ)一些支持性不好的規(guī)則
- 項(xiàng)目根目錄添加tsconfig.json
// tsconfig.json
{
"compilerOptions": {
"target": "es5", // 編譯后的代碼轉(zhuǎn)為 es5 以兼容低版本
"module": "esnext", // 讀取的文件采用的都是 ES6 的模塊系統(tǒng)
"allowSyntheticDefaultImports": true, // 允許從沒(méi)有設(shè)置默認(rèn)導(dǎo)出的模塊中默認(rèn)導(dǎo)入
"experimentalDecorators": true, // 允許使用 裝飾器
"moduleResolution": "node", // 決定如何處理模塊
"strict": true, // 啟用所有嚴(yán)格檢查選項(xiàng)藏姐。 包含--noImplicitAny, --noImplicitThis, --alwaysStrict, --strictBindCallApply, --strictNullChecks, --strictFunctionTypes和--strictPropertyInitialization
"allowJs": true, // 接受JavaScript做為輸入
"skipLibCheck": true, // 忽略所有的聲明文件(*.d.ts)的類(lèi)型檢查
"sourceMap": true,
"importHelpers": true,// 從tslib導(dǎo)入輔助工具函數(shù)
"downlevelIteration": true,
"lib": ["es2015", "dom"], // 編譯過(guò)程中需要引入的庫(kù)文件的列表
"jsx": "react" // 在.tsx文件里支持JSX
},
"include": [ //讀取所有可識(shí)別的src目錄下的文件
"./src/**/*"
],
"exclude": ["*.js", "dist", "node_modules", "ios", "android"] // 不讀取這些文件
}
- 調(diào)整 eslintrc 文件
parser: 'typescript-eslint-parser',
plugins: [
'typescript'
],
- 調(diào)整 vscode 設(shè)置
"eslint.validate": [
"javascript",
"javascriptreact",
"typescript",
"typescriptreact" // jsx 支持
],
- 遇到的一些問(wèn)題:
1隆箩、webpack 中定義了alias后,在文件中使用時(shí)報(bào)"模塊找不到的錯(cuò)誤"羔杨,這時(shí)需要在 tsconfig.json 中定義 baseUrl捌臊,如
// alias
containers: resolve('src/containers'),
// 某tsx文件中
import Com from 'containers/Com' // 這時(shí)會(huì)報(bào)模塊找不到錯(cuò)誤
// 配置tsconfig.json 的 baseUrl 可以解決
baseUrl: './src'
2、在 tsx 文件中引入的 scss兜材、css理澎、png等非 js 文件報(bào)模塊找不到錯(cuò)誤逞力,可以在 src 下新建一個(gè) global.d.ts,內(nèi)容如下:
declare module '*.(scss|png|jpg)' {
const content: any;
export default content;
}
3糠爬、webpack 的 module.hot 報(bào)錯(cuò)掏击,需安裝 @types/webpack-env @types/node
npm i -D @types/webpack-env @types/node
2、基本語(yǔ)法
基礎(chǔ)類(lèi)型
相比于原始JS秩铆,多了 any砚亭,void,never殴玛,元組捅膘,枚舉
let a:any = 'tom'; // 為編程階段還不清楚類(lèi)型的變量指定一個(gè)類(lèi)型
let b:function():void = function(){} // 當(dāng)一個(gè)函數(shù)沒(méi)有返回值時(shí)
let c:function():never = function(){ throw Error('error')} // 返回never的函數(shù)必須存在無(wú)法達(dá)到的終點(diǎn)
let d:[string,number] =['tom',123] // 元組類(lèi)型允許表示一個(gè)已知元素?cái)?shù)量和類(lèi)型的數(shù)組,各元素的類(lèi)型不必相同
enum Color = {Green,Red,Blue} // 枚舉類(lèi)型可以為一組數(shù)值賦予友好的名字
// 其他:類(lèi)型斷言(即類(lèi)型轉(zhuǎn)換)
let someValue: any = "this is a string";
let strLength: number = (someValue as string).length;
// 或者
let strLength: number = (<string>someValue).length;
高級(jí)類(lèi)型
- 交叉類(lèi)型 &
交叉類(lèi)型是將多個(gè)類(lèi)型合并為一個(gè)類(lèi)型
function extend<T, U>(first: T, second: U): T & U {
let result = <T & U>{};
for (let id in first) {
(<any>result)[id] = (<any>first)[id];
}
for (let id in second) {
if (!result.hasOwnProperty(id)) {
(<any>result)[id] = (<any>second)[id];
}
}
return result;
}
- 聯(lián)合類(lèi)型 |
聯(lián)合類(lèi)型表示一個(gè)值可以是幾種類(lèi)型之一,如果一個(gè)值是聯(lián)合類(lèi)型滚粟,我們只能訪問(wèn)此聯(lián)合類(lèi)型的所有類(lèi)型里共有的成員
function padLeft(value: string, padding: string | number) {
// ...
}
- typeof 和 instanceof 類(lèi)型保護(hù)
TypeScript可以將二者識(shí)別為一個(gè)類(lèi)型保護(hù)
function padLeft(value: string, padding: string | number) {
if (typeof padding === "number") {
return Array(padding + 1).join(" ") + value;
}
if (typeof padding === "string") {
return padding + value;
}
throw new Error(`Expected string or number, got '${padding}'.`);
}
// 類(lèi)型為SpaceRepeatingPadder | StringPadder
let padder: Padder = getRandomPadder();
if (padder instanceof SpaceRepeatingPadder) {
padder; // 類(lèi)型細(xì)化為'SpaceRepeatingPadder'
}
if (padder instanceof StringPadder) {
padder; // 類(lèi)型細(xì)化為'StringPadder'
}
// 去除聯(lián)合類(lèi)型中的null,語(yǔ)法是添加!后綴:identifier!從identifier的類(lèi)型里去除了null和undefined
function fixed(name: string | null): string {
function postfix(epithet: string) {
return name!.charAt(0) + '. the ' + epithet; // ok
}
name = name || "Bob";
return postfix("great");
}
- 類(lèi)型別名 type
類(lèi)型別名會(huì)給一個(gè)類(lèi)型起個(gè)新名字寻仗。 類(lèi)型別名有時(shí)和接口很像,但是可以作用于原始值凡壤,聯(lián)合類(lèi)型署尤,元組以及其它任何你需要手寫(xiě)的類(lèi)型。通常用類(lèi)型別名定義聯(lián)合類(lèi)型亚侠,與接口的區(qū)別在于沒(méi)有產(chǎn)生新的類(lèi)型曹体,且不能被繼承和實(shí)現(xiàn)
type a = 'string' | 'number' | null
接口 interface
TypeScript的核心原則之一是對(duì)值所具有的結(jié)構(gòu)進(jìn)行類(lèi)型檢查,接口的作用就是為這些類(lèi)型命名和為你的代碼或第三方代碼定義契約硝烂。接口的2個(gè)作用:1箕别、類(lèi)型檢查;2滞谢、被類(lèi)繼承串稀,強(qiáng)制類(lèi)實(shí)現(xiàn)某種契約
// 基本語(yǔ)法(前 readonly 只讀,后 狮杨? 可選屬性)
interface 類(lèi)型名稱(chēng) {
...
}
- 定義對(duì)象接口
interface SquareConfig {
color?: string;
readonly width?: number;
[index: string]: number;
[propName: string]: any;
}
- 定義函數(shù)接口
// 函數(shù)類(lèi)型檢查器
interface SearchFunc {
(source: string, subString: string): boolean;
}
let mySearch: SearchFunc;
mySearch = function(src, sub) {
let result = src.search(sub);
return result > -1;
}
- 定義數(shù)組接口
// 數(shù)組類(lèi)型 索引檢查器
interface StringArray {
[index: number]: string;
}
let myArray: StringArray;
myArray = ["Bob", "Fred"];
let myStr: string = myArray[0]
- 定義類(lèi)接口
// 類(lèi)類(lèi)型檢查器(當(dāng)一個(gè)類(lèi)實(shí)現(xiàn)了一個(gè)接口時(shí)母截,只對(duì)其實(shí)例部分進(jìn)行類(lèi)型檢查。 constructor存在于類(lèi)的靜態(tài)部分橄教,所以不在檢查的范圍內(nèi)清寇。)
interface ClockInterface {
currentTime: Date;
setTime(d: Date);
}
// 等價(jià)于
declare class ClockInterface {
currentTime: Date;
setTime(d: Date);
}
class Clock implements ClockInterface {
currentTime: Date;
setTime(d: Date) {
this.currentTime = d;
}
constructor(h: number, m: number) { }
}
- 接口繼承接口
interface Shape {
color: string;
}
interface PenStroke {
penWidth: number;
}
interface Square extends Shape, PenStroke {
sideLength: number;
}
let square = <Square>{};
square.color = "blue";
square.sideLength = 10;
square.penWidth = 5.0;
- 接口繼承類(lèi)(會(huì)繼承到類(lèi)的private和protected成員)
class Control {
private state: any;
}
interface SelectableControl extends Control {
select(): void;
}
class Button extends Control implements SelectableControl {
select() { }
}
class TextBox extends Control {
select() { }
}
// Error: Property 'state' is missing in type 'Image'.
class Image implements SelectableControl {
select() { }
}
- 泛型接口
// 函數(shù)泛型接口,比普通函數(shù)接口更為嚴(yán)格
interface GenericIdentityFn<T> {
(arg: T): T;
}
function identity<T>(arg: T): T {
return arg;
}
let myIdentity: GenericIdentityFn<number> = identity;
- 混合接口
一個(gè)對(duì)象可以同時(shí)做為函數(shù)和對(duì)象使用颤陶,并帶有額外的屬性
// 這個(gè)接口既可以當(dāng)類(lèi)接口也可當(dāng)對(duì)象接口
interface Counter {
(start: number): string;
interval: number;
reset(): void;
}
類(lèi)
- 成員修飾符 public , protected, private
所以成員默認(rèn)都是public.
private成員不能在類(lèi)外使用颗管,即不能在實(shí)例上以及子類(lèi)上使用
protected成員可以在子類(lèi)中使用陷遮,不能在自身及其子類(lèi)的實(shí)例上使用滓走。不過(guò)可以通過(guò)子類(lèi)的實(shí)例方法訪問(wèn)
若把構(gòu)造函數(shù)聲明為protected,則該類(lèi)只能被子類(lèi)實(shí)例化帽馋,本身不能實(shí)例化 - 只讀屬性 readonly
只讀屬性必須在聲明時(shí)或構(gòu)造函數(shù)里被初始化 - 參數(shù)屬性
參數(shù)屬性可以方便地讓我們?cè)谝粋€(gè)地方定義并初始化一個(gè)成員搅方,通過(guò)用在構(gòu)造函數(shù)上比吭。參數(shù)屬性通過(guò)給構(gòu)造函數(shù)參數(shù)添加一個(gè)訪問(wèn)限定符來(lái)聲明
class Animal {
constructor(private name: string) { }
move(distanceInMeters: number) {
console.log(`${this.name} moved ${distanceInMeters}m.`);
}
}
- 抽象類(lèi)
abstract 類(lèi)中的抽象方法必須被子類(lèi)所實(shí)現(xiàn)
abstract class Animal {
abstract makeSound(): void;
move(): void {
console.log('roaming the earch...');
}
}
函數(shù)
傳遞給一個(gè)函數(shù)的參數(shù)個(gè)數(shù)必須與函數(shù)期望的參數(shù)個(gè)數(shù)一致
- 可選參數(shù)和默認(rèn)參數(shù)
可選參數(shù)必須跟在必須參數(shù)后面
帶默認(rèn)值的參數(shù)不需要放在必須參數(shù)的后面。 如果帶默認(rèn)值的參數(shù)出現(xiàn)在必須參數(shù)前面姨涡,用戶必須明確的傳入undefined值來(lái)獲得默認(rèn)值
function buildName(firstName: string, lastName?: string) {
if (lastName)
return firstName + " " + lastName;
else
return firstName;
}
function buildName(firstName: string, lastName = "Smith") {
return firstName + " " + lastName;
}
- 剩余參數(shù)
function buildName(firstName: string, ...restOfName: string[]) {
return firstName + " " + restOfName.join(" ");
}
let buildNameFun: (fname: string, ...rest: string[]) => string = buildName;
- 函數(shù)重載
為同一個(gè)函數(shù)提供多個(gè)函數(shù)類(lèi)型定義來(lái)進(jìn)行函數(shù)重載
function pickCard(x: {suit: string; card: number; }[]): number;
function pickCard(x: number): {suit: string; card: number; };
function pickCard(x): any {
// Check to see if we're working with an object/array
// if so, they gave us the deck and we'll pick the card
if (typeof x == "object") {
let pickedCard = Math.floor(Math.random() * x.length);
return pickedCard;
}
// Otherwise just let them pick the card
else if (typeof x == "number") {
let pickedSuit = Math.floor(x / 13);
return { suit: suits[pickedSuit], card: x % 13 };
}
}
泛型
泛型即類(lèi)型變量衩藤,它是一種特殊的變量,只用于表示類(lèi)型而不是值
function identity<T>(arg: T): T {
return arg;
}
我們定義了泛型函數(shù)后涛漂,可以用兩種方法使用赏表。
第一種是,傳入所有的參數(shù)匈仗,包含類(lèi)型參數(shù):
let output = identity<string>("myString"); // type of output will be 'string'
第二種方法更普遍瓢剿。利用了類(lèi)型推論 -- 即編譯器會(huì)根據(jù)傳入的參數(shù)自動(dòng)地幫助我們確定T的類(lèi)型:
let output = identity("myString"); // type of output will be 'string'
如果編譯器不能夠自動(dòng)地推斷出類(lèi)型的話,只能像上面那樣明確的傳入T的類(lèi)型悠轩,在一些復(fù)雜的情況下间狂,這是可能出現(xiàn)的。
- 泛型接口
interface GenericIdentityFn<T> {
(arg: T): T;
}
function identity<T>(arg: T): T {
return arg;
}
let myIdentity: GenericIdentityFn<number> = identity;
- 泛型類(lèi)
class GenericNumber<T> {
zeroValue: T;
add: (x: T, y: T) => T;
}
let myGenericNumber = new GenericNumber<number>();
myGenericNumber.zeroValue = 0;
myGenericNumber.add = function(x, y) { return x + y; };
- 泛型約束
在泛型中使用 extends 關(guān)鍵字
interface Lengthwise {
length: number;
}
function loggingIdentity<T extends Lengthwise>(arg: T): T {
console.log(arg.length); // Now we know it has a .length property, so no more error
return arg;
}
類(lèi)型兼容性
- 普通類(lèi)型和對(duì)象
要將y復(fù)制給x火架,必須保證x的每個(gè)屬性都在y中鉴象,且類(lèi)型匹配。對(duì)于y多余的屬性則不管何鸡。
interface Named {
name: string;
}
let x: Named;
let y = { name: 'Alice', location: 'Seattle' };
x = y;
- 函數(shù)兼容
與上面剛好相反
let x = (a: number) => 0;
let y = (b: number, s: string) => 0;
y = x; // OK
x = y; // Error
模塊
模塊的導(dǎo)入導(dǎo)出和ES6一致纺弊,import export,但也有新增部分
- 整體導(dǎo)出
使用export =導(dǎo)出一個(gè)模塊骡男,則必須使用TypeScript的特定語(yǔ)法import module = require("module")來(lái)導(dǎo)入此模塊俭尖。
let numberRegexp = /^[0-9]+$/;
class ZipCodeValidator {
isAcceptable(s: string) {
return s.length === 5 && numberRegexp.test(s);
}
}
export = ZipCodeValidator;
// 導(dǎo)入
import zip = require("./ZipCodeValidator");
- 使用外部模塊
node里大部分模塊都不是TS寫(xiě)的,如果要用就需要為這些模塊做個(gè)TS聲明洞翩,一般放在.d.ts文件里:
// node.d.ts
declare module "url" {
export interface Url {
protocol?: string;
hostname?: string;
pathname?: string;
}
export function parse(urlStr: string, parseQueryString?, slashesDenoteHost?): Url;
}
declare module "path" {
export function normalize(p: string): string;
export function join(...paths: any[]): string;
export let sep: string;
}
現(xiàn)在我們可以/// <reference> node.d.ts并且使用import url = require("url");或import * as URL from "url"加載模塊稽犁。
/// <reference path="node.d.ts"/>
import * as URL from "url";
let myUrl = URL.parse("http://www.typescriptlang.org");
假如你不想在使用一個(gè)新模塊之前花時(shí)間去編寫(xiě)聲明,你可以采用聲明的簡(jiǎn)寫(xiě)形式以便能夠快速使用它骚亿。
declare module "hot-new-module";
import x, {y} from "hot-new-module"; // 該模塊的導(dǎo)出所有類(lèi)型都將會(huì)是any
x(y);
- 導(dǎo)入其他類(lèi)型文件已亥,如txt,json等
declare module "*!text" {
const content: string;
export default content;
}
// Some do it the other way around.
declare module "json!*" {
const value: any;
export default value;
}
//
import fileContent from "./xyz.txt!text";
import data from "json!http://example.com/data.json";
console.log(data, fileContent);
- 擴(kuò)展模塊
// map.ts
import { Observable } from "./observable";
declare module "./observable" {
interface Observable<T> {
map<U>(f: (x: T) => U): Observable<U>;
}
}
Observable.prototype.map = function (f) {
// ... another exercise for the reader
}
// consumer.ts
import { Observable } from "./observable";
import "./map";
let o: Observable<number>;
o.map(x => x.toFixed());
// observable.ts
export class Observable<T> {
// ... still no implementation ...
}
declare global { // 全局?jǐn)U展
interface Array<T> {
toObservable(): Observable<T>;
}
}
Array.prototype.toObservable = function () {
// ...
}
一些操作符和關(guān)鍵字
- keyof T 索引類(lèi)型查詢操作符
keyof T的結(jié)果為T(mén)上已知的公共屬性名的聯(lián)合 - T[K] 索引訪問(wèn)操作符
T[K] 返回屬性的類(lèi)型 - extend 繼承
接口繼承接口来屠,接口繼承類(lèi)虑椎,類(lèi)繼承類(lèi) - implement 實(shí)現(xiàn)
類(lèi)實(shí)現(xiàn)接口 - extends 泛型約束關(guān)鍵字
- new
function createInstance<A>(c: new () => A): A {
return new c();
}
function createInstance<A>(c: {new(): T; }): A {
return new c();
}
常見(jiàn)問(wèn)題
- 枚舉類(lèi)型key-value互轉(zhuǎn)
enum Color {Red = 1, Green = 2, Blue = 4}
let c: Color = Color.Green; // 2
let colorName: string = Color[Color.Green]; // 'Green'
- 給回調(diào)函數(shù)聲明類(lèi)型
type Cb = () => void;
on(type: string, cb: Cb);
- 自己寫(xiě) 類(lèi)型聲明文件
// 首先聲明一下模塊:
declare module 'progressbar.js' {
// 模塊中暴露了 Circle 類(lèi)
export class Circle {
constructor(container: HTMLElement, options: Options);
}
// 構(gòu)造函數(shù)的 Options 需要單獨(dú)聲明
interface Options {
easing?: string;
strokeWidth?: number;
trailColor?: string;
trailWidth?: number;
}
}
- 接口屬性定義順序
// 只讀、必選俱笛、可選捆姜、未知屬性
interface iProps {
readonly x: number;
readonly y: number;
name: string;
age: number;
height?: number;
[propName: string]: any;
}
- 小技巧
// 定義一個(gè)javascript的對(duì)象,key是字符串迎膜,value是任意類(lèi)型
const people:Record<string,any> = {
name: 'chengfeng',
age: 10
}
// 將傳入的屬性變?yōu)榭蛇x項(xiàng)
interface IPeople {
title: string;
name: string;
}
const people: Partial<IPeople> = {
title: 'Delete inactive users',
};
// 傳入的屬性變?yōu)樽兂芍蛔x
const people: Readonly<IPeople> = {
title: 'todo list',
name: chenfeng;
};
const people1: Partial<IPeople> = { title: 'ts' }; // OK
const people22: Required<IPeople> = { title: 'ts' }; // Error: property 'name' missing
type T = keyof IPeople // -> "name" | "age"
// 結(jié)合 react-router-dom 使用
import { withRouter, RouteComponentProps } from 'react-router-dom';
class App extends React.Component<IProps & RouteComponentProps<{}>, AppStates> {}
export default withRouter(App);
- any 與 object 類(lèi)型的區(qū)別
Object類(lèi)型的變量只是允許你給它賦任意值泥技,但是卻不能夠在它上面調(diào)用任意的方法,即便它真的有這些方法磕仅;any 可以 - readonly 與 const
做為變量使用的話用const珊豹,若做為屬性則使用readonly - 對(duì)象字面量的TS檢測(cè)
對(duì)象字面量會(huì)被特殊對(duì)待而且會(huì)經(jīng)過(guò)額外屬性檢查簸呈,當(dāng)將它們賦值給變量或作為參數(shù)傳遞的時(shí)候。 如果一個(gè)對(duì)象字面量存在任何“目標(biāo)類(lèi)型”不包含的屬性時(shí)店茶,你會(huì)得到一個(gè)錯(cuò)誤蜕便。可以用索引簽名解決這個(gè)問(wèn)題:
interface SquareConfig {
color?: string;
width?: number;
[propName: string]: any; // 可包含任何屬性
}
- 類(lèi)作為接口
類(lèi)可以產(chǎn)生接口贩幻,類(lèi)分為靜態(tài)部分和實(shí)例部分
class Clock implements ClockInterface {
currentTime = new Date()
setTime(d: Date) {
this.currentTime = d
}
reset() {}
a = 1
constructor(h: number, m: number) {}
}
let clock: typeof Clock = Clock // typeof Clock 取得是 構(gòu)造函數(shù) 的類(lèi)型
3轿腺、React 中使用的最佳實(shí)踐
- vscode 安裝 Typescript React code snippets 插件
- 所有用到 jsx 語(yǔ)法的文件都需要以 tsx 后綴命名
- 使用組件聲明時(shí)的Component<P, S>泛型參數(shù)聲明,來(lái)代替PropTypes丛楚!
- 全局變量或者自定義的 window 對(duì)象屬性吃溅,統(tǒng)一在項(xiàng)目根下的 global.d.ts 中進(jìn)行聲明定義
- 對(duì)于項(xiàng)目中常用到的接口數(shù)據(jù)對(duì)象,在schemas/目錄下定義好其結(jié)構(gòu)化類(lèi)型聲明
- 類(lèi)組件的聲明
class App extends Component<IProps, IState> {
static defaultProps = {
// ...
}
readonly state = { // 使用 class properties 語(yǔ)法對(duì)state做初始化時(shí)鸯檬,會(huì)覆蓋掉Component<P, S>中對(duì)state的readonly標(biāo)識(shí)决侈,所以需要顯示寫(xiě)上 readonly
// ...
};
// 小技巧:如果state很復(fù)雜不想一個(gè)個(gè)都初始化,可以結(jié)合類(lèi)型斷言初始化state為空對(duì)象或者只包含少數(shù)必須的值的對(duì)象: readonly state = {} as IState;
}
- 函數(shù)式組件的聲明
// SFC: stateless function components
// v16.7起喧务,由于hooks的加入赖歌,函數(shù)式組件也可以使用state,所以這個(gè)命名不準(zhǔn)確功茴。新的react聲明文件里庐冯,也定義了React.FC類(lèi)型^_^
const List: React.SFC<IProps> = props => null
- 正確的聲明高階組件
import { RouteComponentProps } from 'react-router-dom';
// 方法一
@withRouter
class App extends Component<Partial<RouteComponentProps>> {
public componentDidMount() {
// 這里就需要使用非空類(lèi)型斷言了
this.props.history!.push('/');
}
// ...
});
// 方法二
@withRouter
class App extends Component<{}> {
get injected() {
return this.props as RouteComponentProps
}
public componentDidMount() {
this.injected.history.push('/');
}
// ...
interface IVisible {
visible: boolean;
}
//排除 IVisible
function withVisible<Self>(WrappedComponent: React.ComponentType<Self & IVisible>): React.ComponentType<Omit<Self, 'visible'>> {
return class extends Component<Self> {
render() {
return <WrappedComponent {...this.props} visible={true} />
}
}
}
- react-router-dom 中常用 RouteComponentProps,react中常用的 ReactNode
// IProps 常用寫(xiě)法
interface IProps extends RouteComponentProps {
title: string | ReactNode
style: any
}
// 或者
type IProps = {
style: any
config: {}[]
}
// IState 狀態(tài)聲明常用寫(xiě)法
const defaultState = {$1}
type IState = Readonly<typeof defaultState>
- 在組件定義的地方定義接口坎穿,而不是使用的時(shí)候展父,否則需要定義兩遍
// 定義處
type IProps = {
style?: any
config: {}[]
}
export default class Paragraph extends PureComponent<IProps, {}> {
render() {
const { style, config } = this.props
return (
<div className={styles.layout} style={style}>
{config.map((item, index) => (
<p className={styles.paragraph} key={index} style={style}>
{item}
</p>
))}
</div>
)
}
}
// 使用處
<Paragraph config={this.config.subTitle} />
- 泛型IProps
上述IProps中,如果config是多類(lèi)型的對(duì)象數(shù)組玲昧,則可以這樣
type IProps<T> = {
style?: any
config: T[]
}
export default class Paragraph<T> extends PureComponent<IProps<T>, {}> {
render() {
const { style, config } = this.props
return (
<div className={styles.layout} style={style}>
{(config as T[]).map((item, index) => (
<p className={styles.paragraph} key={index} style={style}>
{item}
</p>
))}
</div>
)
}
}
參考
https://github.com/plantain-00/blogs/issues/28
https://tasaid.com/blog/20171102225101.html
https://juejin.im/entry/5a156adaf265da43231aa032
typescript 高級(jí)技巧
https://mp.weixin.qq.com/s/_lO3cd0FcF0Dg3TRnHPdwg)
TypeScript 模塊解析