1.new關鍵字在類型中的使用
泛型
在泛型里使用類類型
在TypeScript使用泛型創(chuàng)建工廠函數時万皿,需要引用構造函數的類類型。比如,
function create<T>(c: {new(): T; }): T {//這邊的new()不好理解
return new c();
}
一個更高級的例子,使用原型屬性推斷并約束構造函數與類實例的關系门烂。
class BeeKeeper {
hasMask: boolean;
}
class ZooKeeper {
nametag: string;
}
class Animal {
numLegs: number;
}
class Bee extends Animal {
keeper: BeeKeeper;
}
class Lion extends Animal {
keeper: ZooKeeper;
}
function createInstance<A extends Animal>(c: new () => A): A {
return new c();
}
createInstance(Lion).keeper.nametag; // typechecks!
createInstance(Bee).keeper.hasMask; // typechecks!
查了不少資料,比較好的解釋是what is new() in Typescript?意思就是create
函數的參數是構造函數沒有參數的T類的類型,同理兄淫,createInstance
函數的參數是構造函數沒有參數的A類的類型诅福。
帶著疑問寫了測試代碼:
vscode依然報錯,仔細想下拖叙,
createInstance
函數return new c();
這句話是類的實例化,所以傳進來的參數c
是個類赂乐,而不是類的實例薯鳍,故要使用(c: new () => A)
標明c是個類,而不是(c: Animal)
類的實例挨措,從下面的調用也可以看出傳遞的是類而不是實例挖滤。我們知道js里面是沒有類的,ES6里面的class也只是個語法糖浅役,編譯后依然為一個function斩松。所以去修飾一個class也就是修飾一個function,但是修飾的是構造函數觉既,所以這邊加以區(qū)別惧盹,前面有個new。
接口
類靜態(tài)部分與實例部分的區(qū)別
這邊同樣用到了關鍵字new()
第一個例子報錯了瞪讼,
官方解釋:
當你操作類和接口的時候钧椰,你要知道類是具有兩個類型的:靜態(tài)部分的類型和實例的類型。 你會注意到符欠,當你用構造器簽名去定義一個接口并試圖定義一個類去實現這個接口時會得到一個錯誤:
這里因為當一個類實現了一個接口時嫡霞,只對其實例部分進行類型檢查。 constructor存在于類的靜態(tài)部分希柿,所以不在檢查的范圍內诊沪。
因此养筒,我們應該直接操作類的靜態(tài)部分。 看下面的例子端姚,我們定義了兩個接口晕粪, ClockConstructor為構造函數所用和ClockInterface為實例方法所用。 為了方便我們定義一個構造函數 createClock寄锐,它用傳入的類型創(chuàng)建實例兵多。
interface ClockConstructor {
new (hour: number, minute: number): ClockInterface;
}
interface ClockInterface {
tick:()=>void;
}
function createClock(ctor: ClockConstructor, hour: number, minute: number): ClockInterface {//這個和泛型中使用類類型相同,
return new ctor(hour, minute);//需要類型為ClockInterface的兩個參數的構造器類橄仆,只是二者寫法有點區(qū)別
}
class DigitalClock implements ClockInterface {//這邊實現的接口不能是直接的構造器
constructor(h: number, m: number) { }
tick() {
console.log("beep beep");
}
}
class AnalogClock implements ClockInterface {
constructor(h: number, m: number) { }
tick() {
console.log("tick tock");
}
}
let digital = createClock(DigitalClock, 12, 17);
let analog = createClock(AnalogClock, 7, 32);
因為createClock的第一個參數是ClockConstructor類型剩膘,在createClock(AnalogClock, 7, 32)里,會檢查AnalogClock是否符合構造函數簽名盆顾。
再結合react官方接口的書寫怠褐,
interface Component<P = {}, S = {}> extends ComponentLifecycle<P, S> { }
class Component<P, S> {//這里全部是實例方法和屬性
constructor(props?: P, context?: any);
// Disabling unified-signatures to have separate overloads. It's easier to understand this way.
// tslint:disable:unified-signatures
setState<K extends keyof S>(f: (prevState: S, props: P) => Pick<S, K>, callback?: () => any): void;
setState<K extends keyof S>(state: Pick<S, K>, callback?: () => any): void;
// tslint:enable:unified-signatures
forceUpdate(callBack?: () => any): void;
render(): JSX.Element | null | false;
// React.Props<T> is now deprecated, which means that the `children`
// property is not available on `P` by default, even though you can
// always pass children as variadic arguments to `createElement`.
// In the future, if we can define its call signature conditionally
// on the existence of `children` in `P`, then we should remove this.
props: Readonly<{ children?: ReactNode }> & Readonly<P>;
state: Readonly<S>;
context: any;
refs: {
[key: string]: ReactInstance
};
}
interface ComponentClass<P = {}> {
new (props?: P, context?: any): Component<P, ComponentState>;//此處對Component做了修飾,規(guī)定react的構造函數類型
propTypes?: ValidationMap<P>;//下面幾個全部是靜態(tài)方法和屬性
contextTypes?: ValidationMap<any>;
childContextTypes?: ValidationMap<any>;
defaultProps?: Partial<P>;
displayName?: string;
}
為什么寫了interface Component又寫了class Component,interface Component沒有寫里面具體的實例方法和屬性,而寫了同名的class Component,是不是意味著class Component就是interface的具體實現浪汪,這里是index.d.ts文件盗蟆,應該在.ts文件里面不能這么寫
到此new()
關鍵字在類型中的使用基本搞清楚了。
類裝飾器
function classDecorator<T extends {new(...args:any[]):{}}>(constructor:T) {
return class extends constructor {
newProperty = "new property";
hello = "override";
}
}
@classDecorator
class Greeter {
property = "property";
hello: string;
constructor(m: string) {
this.hello = m;
}
}
console.log(new Greeter("world"));
2. 裝飾器的使用
裝飾器(Decorator)在React中的應用
JavaScript 中的裝飾器是什么议谷?
3. 函數類型聲明的不同方式
1. 最常見的方式
函數聲明(Function Declaration)類型的,就是普通的具名函數
function add(x: number, y: number): number {
return x + y
}
2. 函數表達式(Function Expression)類型聲明
- 1.這種就是后面賦值號后面有個匿名或者具名函數,比較好理解的寫法,都在函數體內寫類型
handle = (
baseValue: number,
increment: number,
): number => {
return baseValue
}
- 2.給變量定義類型极祸,同時也在函數內部定義類型
handle: (baseValue: number, increment: number) => number = (
baseValue: number,
increment: number,
): number => {
return baseValue
}
- 3.將變量的類型抽取到接口中
interface IHandle {
(baseValue: number, increment: number): number
}
handle: IHandle = (baseValue: number, increment: number): number => {
return baseValue
}
既然前面的變量聲明了接口,那么后面的函數里面的類型就可以去掉了
interface IHandle {
(baseValue: number, increment: number): number
}
handle: IHandle = (baseValue,increment)=> {
return baseValue
}
但是發(fā)現個問題
interface IHandle {
(baseValue: number, increment: number): number
}
handle: IHandle = (baseValue)=> {
return baseValue
}
這么寫居然vscode沒有報錯怠晴,increment明明不是可選參數遥金,不是很理解,有待討論
查閱了typescript的官方文檔的函數章節(jié)
interface UIElement {
addClickListener(onclick: (this: void, e: Event) => void): void;
}
這么寫的接口蒜田,和函數類型的接口聲明驚人的相似稿械,
interface IHandle {
(baseValue: number, increment: number): number
}
差別在哪呢.函數類型的接口聲明是匿名的,而上面對象的類型聲明的函數是具名的冲粤,做了一下測試
var src:UIElement = function() {}
//報錯:
// Type '() => void' is not assignable to type 'UIElement'.
// Property 'addClickListener' is missing in type '() => void'.
// var src: UIElement
interface UIElement {
addClickListener(name: string): void
}
var src: UIElement = {//不報錯
addClickListener() {},
}
var src: UIElement = { //報錯
addClickListener(name: string, age: number) {},
}
// Type '{ addClickListener(name: string, age: number): void; }' is not assignable to type 'UIElement'.
// Types of property 'addClickListener' are incompatible.
// Type '(name: string, age: number) => void' is not assignable to type '(name: string) => void'.
看樣就是實際的函數的參數可以比接口少定義美莫,但是不能多定義
函數接口的是用來修飾變量的,當然包括函數的形參的修飾色解,以及返回值的修飾茂嗓,但是不能修飾具名函數
interface IHandle {
props?: object
(baseValue: number, increment: number): number
}
function source:IHandle(baseValue) {//這么修飾具名函數會報錯
return baseValue
}
function source(baseValue): IHandle {//這么寫是可以的,表明返回了一個IHandle 的函數
return baseValue
}
3. 怎樣修飾類(類類型)
class Greeter {
static standardGreeting = "Hello, there";
greeting: string;
greet() {
if (this.greeting) {
return "Hello, " + this.greeting;
}
else {
return Greeter.standardGreeting;
}
}
}
let greeter1: Greeter;
greeter1 = new Greeter();
console.log(greeter1.greet());
let greeterMaker: typeof Greeter = Greeter;
greeterMaker.standardGreeting = "Hey there!";
let greeter2: Greeter = new greeterMaker();
console.log(greeter2.greet());
- 我們一般都是修飾一個類的實例的科阎,怎么簡單的修飾類述吸,typeof是個很好的辦法,
這個例子里,greeter1與之前看到的一樣蝌矛。 我們實例化 Greeter類道批,并使用這個對象。 與我們之前看到的一樣入撒。
再之后隆豹,我們直接使用類。 我們創(chuàng)建了一個叫做 greeterMaker的變量茅逮。 這個變量保存了這個類或者說保存了類構造函數璃赡。 然后我們使用 typeof Greeter,意思是取Greeter類的類型献雅,而不是實例的類型碉考。 或者更確切的說,"告訴我 Greeter標識符的類型"挺身,也就是構造函數的類型侯谁。 這個類型包含了類的所有靜態(tài)成員和構造函數。 之后章钾,就和前面一樣墙贱,我們在 greeterMaker上使用new,創(chuàng)建Greeter的實例贱傀。
也就是使用typeof ClassName
interface IPerson {
age: number;
}
class Person {
age: 99;
}
let p: typeof IPerson = Person;//這么寫會報錯的
- 使用構造函數接口修飾類
interface IPerson {
age: number;
}
interface IPersonConstructor {
new (): IPerson;
}
class Person {
age: 99;
}
let p: IPersonConstructor = Person;
之前對new (): IPerson;
這句話后面的返回值不是很理解惨撇,直到看到了將基類構造函數的返回值作為'this',也就是說new Person()的時候執(zhí)行的是構造函數府寒,那么構造函數就返回了Person的實例串纺,自然new (): IPerson;
構造函數返回IPerson就很好理解了
4. keyof
比如有個interface a{
a1: 'a1';
a2: 'a2';
.....
.....
a100: 'a100';
}
然后又個類型要繼承這個interface的某一個value的值
比如 type anum = 'a1' | 'a2' | 'a3' | ....| 'a100',
應該怎么寫?
type anum = typeof a.a1 | typeof a.a2 | typeof a.a3 | ....| typeof a.a100;
有沒有簡單的寫法 那個interface 有可能隨時在變
a1到a100全要椰棘?
對全要
而且有可能 到a100以上 一直在添加
我想在添加的時候只添加interface type不用在修改了 有沒有辦法
key還是value
value
5. 函數名后面的感嘆號(非空斷言操作符)
onOk = () => {
this.props.onOk!(this.picker && this.picker.getValue());
this.fireVisibleChange(false);
}
react-component/m-picker
群里問了是非空操作符
再去搜索,在typescript 2.0的文檔里找到了榄笙,叫 非空斷言操作符
// 使用--strictNullChecks參數進行編譯
function validateEntity(e?: Entity) {
// 如果e是null或者無效的實體邪狞,就會拋出異常
}
function processEntity(e?: Entity) {
validateEntity(e);
let s = e!.name; // 斷言e是非空并訪問name屬性
}
需要在tsconfig.json 里面加上"strictNullChecks": true,這樣vscode會自動檢測null和undefined
6. ts差集運算
Add support for literal type subtraction
具體應用在高階組件里面的props
TypeScript在React高階組件中的使用技巧
7. 方法重寫
方法重寫子類的參數需要跟父類一致,否則會報錯
8. 剩余參數
const renderWidget = ({ field, widget, ...restParams }) => {
const min = restParams.min;
};
這段代碼的剩余參數restParams 怎么表示
感覺應該這么寫
const renderWidget = (
{
field,
widget,
...restParams
}: { field: string; widget: string;restParams: [key: string]: any },
idx: number,
primaryField: string
) => {
const min = restParams.min;
};
但是還是報錯茅撞,其實應該省去restParams
const renderWidget = ({
field,
widget,
...restParams
}: {
field: string;
widget: string;
[key: string]: any;
}) => {
const min = restParams.min;
};
這樣就好了Destructuring a function parameter object and …rest
9. 元組推斷
群里看到的疑問帆卓,為什么最后推斷出是string|number
type TTuple = [string, number];
type Res = TTuple[number]; // string|number
一開始不理解,自己改下寫法
type TTuple = [string, number];
type Res = TTuple[string];
[圖片上傳失敗...(image-894ad4-1590418731585)]
再嘗試寫下去
type TTuple = [string, number];
type Res = TTuple[0];//string
type Res1 = TTuple[1];//number
type Res2 = TTuple[2];//報錯
[圖片上傳失敗...(image-e6ec46-1590418731585)]
TTuple[2]
報錯
首先js中沒有元組概念米丘,數組中可以存放任何數據類型剑令,ts中把存放不同數據類型的數組叫做元組,這樣就很好理解TTuple[number]
中的number就是索引index拄查,索引只能是數字吁津,而且不能越界,所以兩次報錯就好理解了,TTuple[number]
為什么返回string|number
是因為沒有指定具體的索引碍脏,只能推斷出兩種可能梭依,string或number
聯想到獲取接口的屬性的類型
interface IProps {
name: string;
age: number;
}
type IAge = IProps["name"];// string
const per: IAge = "geek";
10. type interface 泛型差異
下面實現類似于Record的代碼
interface Collection<T extends string, U> {
[P in T]: U;
}
type Collection33<K extends string, T> = {
[P in K]: T;
};
type完全沒問題,interface 的兩種寫法都會報錯典尾,好奇葩