寫了3個月TypeScript,我學到了什麼?

寫了3個月TypeScript,我學到了什麼?.png

原文連接:TypeScript入門css

以前閱讀vue源碼的時候發現有TypeScript,一臉懵逼,所以須要入個門。html

最近在新環境的平常工做中也須要用到TypeScript,學習過程當中遇到一些疑惑,作了記錄。前端

我的以爲仍是比較適合TypeScript入門的同窗閱讀的,由於我遇到的這些疑惑,可能你也會遇到。vue

  • ts類型中的?,<>意思是什麼?
  • 什麼是duck typing?
  • constructor以前的變量定義是什麼?
  • declare是什麼?
  • ts中unknown, void, null和undefined,never區別是什麼?
  • ts中的泛型約束是什麼?
  • 數組類型的兩種定義方式
  • ts中的類型斷言
  • 泛型函數與泛型接口
  • 如何理解as const?
  • declare global是什麼意思?
  • 如何在TypeScript環境增長一個全局變量?
  • interface能夠繼承嗎?
  • typescript中的&是什麼意思?
  • interface與type的區別是什麼?
  • enum做爲一種類型是什麼意思?
  • 項目中xxx.d.ts的declare module '*.scss'是什麼意思?declare module還能夠作什麼?
  • typescript如何約束Promise的類型?
  • typescript中的keyof如何使用?
  • typescript中的typeof如何使用?
  • typescript中的non-null operator是什麼?

ts類型中的?意思是什麼?

// https://github.com/vuejs/vue/blob/dev/src/core/observer/watcher.js
before: ?Function;
options?: ?Object,
複製代碼

這是ts的interface中的一個概念。ts的interface就是"duck typing"或者"structural subtyping",類型檢查主要關注the shape that values have。所以咱們先來熟悉一下interface,再引出?的解釋。java

TypeScript普通方式定義函數:
function print(obj: {label: string}) {
    console.log(obj.label);
}
let foo = {size: 10, label: "這是foo, 10斤"};
print(foo);
複製代碼
TypeScript interface方式定義函數:
interface labelInterface {
    label: string;
}
function print(obj: labelInterface) {
    console.log(obj.label);
}
let foo = {size: 10, label: "這是foo, 10斤"};
print(foo);
複製代碼

進入正題,TypeScript中的是什麼意思?Optional Properties。node

Optional Properties
  • 並非interface中的全部屬性都是required的,一些存在特定條件下,一些根本不存在。
  • Optional Properties適用於"option bags"的設計模式,這種設計模式意思是:咱們傳遞一個對象到函數,這個函數只有幾個屬性,沒有其餘更多的屬性。
  • Optional Property的好處在於,清晰的看清楚有哪些屬性,防止傳入不屬於該interface的屬性。
interface SquareConfig {
    color?: string;
    width?: number;
}
function createSquare(config: SquareConfig): {color: string; area: number} {
    let newSquare = {color: "white", area: 100};
    if (config.clor) {
        // Error: Property 'clor' does not exist on type 'SquareConfig'
        newSquare.color = config.color;
    }
    if (config.width) {
        newSquare.area = config.width * config.width;
    }
    return newSquare;
}
let mySquare = createSquare({color: "black"});
複製代碼

Interfaces with optional properties are written similar to other interfaces, with each optional property denoted by a ? at the end of the property name in the declaration.python

什麼是?和Optional Properties呢?interface的某些非required屬性名的末尾,添加?這是一個optional property,其實就是字面意思,條件屬性。react

Optional Property只是屬性名,也就是options?: ?Object,中options後的問號,拿屬性值類型前的問號是什麼意思,也就是?Object,是什麼意思? 此處的問號表明屬性值類型是否能夠是null類型,可是隻有strictNullChecks爲on時,值類型才能爲null。git

/** * @type {?number} * strictNullChecks: true -- number | null * strictNullChecks: off -- number * */
  var nullable;
複製代碼

咱們的例子中,options?:?Object的意思是options的值類型能夠是Object,null(僅在strictNullChecks爲true時容許)。es6

ts類型中的<>什麼意思?

deps: Array<Dep>a
newDeps: Array<Dep>
複製代碼

ts中的數組類型與java中的定義相似:

let list: number[] = [1, 2, 3];
let list: Array<number> = [1, 2, 3];
複製代碼

什麼是duck typing?

duck test。若是"走路像鴨子,叫聲像鴨子,那麼這就是鴨子"。 在computer programming,用於'判斷對象是否能夠按照預期的目的使用'。 一般的typing中,適用性取決於對象的type。duck typing不同,對象的適用性取決於指定method或property的存在與否,而不是取決於對象自身的類型。

前端工程師基本都是duck typing,由於JavaScript沒有type。 --這話是我說的

Python3 example
class Duck:
    def fly(self):
        print("Duck flying")

class Airplane:
    def fly(self):
        print("Airplane flying")

class Whale:
    def swim(self):
        print("Whale swimming")

def lift_off(entity):
    entity.fly()

duck = Duck()
airplane = Airplane()
whale = Whale()

lift_off(duck) # prints `Duck flying`
lift_off(airplane) # prints `Airplane flying`
lift_off(whale) # Throws the error `'Whale' object has no attribute 'fly'`
複製代碼
Javascript example
class Duck {
    fly() {
        console.log("Duck flying")
    }
}
class Airplane {
    fly() {
        console.log("Airplane flying")
    }
}
class Whale {
    swim() {
        console.log("Whale swimming")
    }
}

function liftOff(entity) {
    entity.fly()
}

const duck = new Duck();
const airplane = new Airplane();
const whale = new Whale();

liftOff(duck); // Duck flying
liftOff(airplane); // Airplane flying
liftOff(whale); // Uncaught TypeError: entity.fly is not a function
複製代碼

constructor以前的變量定義是什麼?

例如vnode的定義:

export default class VNode {
  tag: string | void;
  data: VNodeData | void;
  children: ?Array<VNode>;
  text: string | void;
  elm: Node | void;
  ns: string | void;
  context: Component | void; // rendered in this component's scope
  key: string | number | void;
  componentOptions: VNodeComponentOptions | void;
  componentInstance: Component | void; // component instance
  parent: VNode | void; // component placeholder node

  // strictly internal
  raw: boolean; // contains raw HTML? (server only)
  isStatic: boolean; // hoisted static node
  isRootInsert: boolean; // necessary for enter transition check
  isComment: boolean; // empty comment placeholder?
  isCloned: boolean; // is a cloned node?
  isOnce: boolean; // is a v-once node?
  asyncFactory: Function | void; // async component factory function
  asyncMeta: Object | void;
  isAsyncPlaceholder: boolean;
  ssrContext: Object | void;
  fnContext: Component | void; // real context vm for functional nodes
  fnOptions: ?ComponentOptions; // for SSR caching
  fnScopeId: ?string; // functional scope id support

  constructor ()
...
}
複製代碼

www.typescriptlang.org/docs/handbo… typeScript中的class要比es6的多一項:property。這和java或者c#中的一致。

property
constructor
method
複製代碼

實際上es6提供了一種私有變量,僅僅能在class內部訪問。

class Rectangle {
  #height = 0;
  #width;
  constructor(height, width) {    
    this.#height = height;
    this.#width = width;
  }
}
複製代碼

冒號後面的:VNode什麼意思?

export function cloneVNode (vnode: VNode): VNode {
...
}
複製代碼

TypeScript中的函數返回值類型。

declare是什麼?

聲明這是一個definition。

  • declare是ts中用於寫定義文件的關鍵字。
  • declare能夠定義全局變量,全局函數,全局命名空間,class等等。
  • declare能夠按照下面這樣去使用:
declare var foo:number;
declare function greet(greeting: string): void;
declare namespace myLib {
    function makeGreeting(s: string): string;
    let numberOfGreeting: number;
}
declare function getWidget(n: number): Widget;
declare function getWidget(s: string): Widget[];
declare class Greeter {
    constructor(greeting: string);

    greeting: string;
    showGreeting(): void;
}
複製代碼

ts中any,unknown, void, null和undefined,never區別是什麼?

null,undefined就是js中的意思。

any: 任意類型,謹慎使用,避免使typescript變成anyscript unknown: 與any相似,可是比any更加安全 void: 一般用於返回值的函數 never:never occur 歷來不會發生的類型,例如永遠不會有結果的,拋出異常或者死循環。

ts中的泛型約束是什麼?

基於string(boolean, Function)類型

function loggingIdentity<T extends string>(arg: T): T {
    console.log(arg.length);
    return arg;
}

loggingIdentity("hello"); // 5
loggingIdentity(2); // Argument of type 'number' is not assignable to parameter of type 'string'.
複製代碼

基於自定義的interface

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;
}

loggingIdentity(3);  // Error, number doesn't have a .length property
loggingIdentity({length: 10, value: 3}); // 10
複製代碼

ts2.8發佈說明

// https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-8.html
type TypeName<T> = T extends string
  ? "string"
  : T extends number
  ? "number"
  : T extends boolean
  ? "boolean"
  : T extends undefined
  ? "undefined"
  : T extends Function
  ? "function"
  : "object";

type T0 = TypeName<string>; // "string"
type T1 = TypeName<"a">; // "string"
type T2 = TypeName<true>; // "boolean"
type T3 = TypeName<() => void>; // "function"
type T4 = TypeName<string[]>; // "object"
複製代碼

同時支持type和interface兩種類型的泛型約束

interface reduxModel<T> {
    reducers: T extends string ? {[x in T]: () => void}: T,
}

type TType = "foo" | "bar" | 'baz'
interface TInterface {
    "foo": () => void,
    "bar": () => void,
    'baz': () => void
}

const ireducers = {
    "foo": () => void
}

const model : reduxModel<TType> = {
    reducers: ireducers
    // 正常運行
}

const model : reduxModel<TInterface> = {
    reducers: ireducers
    // Type '{ foo: () => undefined; }' is missing the following properties from type 'TInterface': "bar", 'baz'
}

複製代碼

數組類型的兩種定義方式

Array<類型>

Array後面加一個<>,<>內聲明元素類型。

type Foo= Array<string>;
複製代碼
interface Bar {
     baz: Array<{
          name: string,
          age: number,
     }>
}
複製代碼

類型[]

元素類型後面加一個[]。

type Foo = string[]
複製代碼
interface Bar {
    baz : {
          name: string,
          age: number,
     }[]
}
複製代碼

ts中的類型斷言

TypeScript容許咱們覆蓋推斷和分析出的視圖類型爲咱們想要的任意方式,這種機制叫作類型斷言(Type Assertion),類型斷言會告訴編譯器你比它更加知道具體是哪一種類型,編譯器不用再二次推斷了。 類型斷言每每是發生在編譯器編譯期間,用於提示編譯器如何分析咱們的代碼。

  • 語法
  • 遷移js代碼
  • 類型斷言的問題
  • 指定event類型
  • 慎用as any和as unknown
  • type與類型斷言

語法

interface Foo {
    name: string,
}
type Any = any;

let a:Foo = {} as Foo;
let a:Foo = {} as Any;
複製代碼

any是任意類型的子類型,因此任意類型均可以被as any,仍是建議謹慎使用,避免變爲anyscript。

遷移js代碼

var foo = {};
foo.bar = 123; // Error: property 'bar' does not exist on `{}`
foo.bas = 'hello'; // Error: property 'bas' does not exist on `{}`
複製代碼
interface Foo {
    bar: number;
    bas: string;
}
var foo = {} as Foo;
foo.bar = 123;
foo.bas = 'hello';  // 註釋掉這一行也不會報錯
複製代碼

類型斷言的問題

foo.bas = 'hello'; // 註釋掉這一行也不會報錯 若是是下面的方式就會報錯了,會提示缺乏bas的定義

interface Foo {
    bar: number;
    bas: string;
}
var foo : Foo= {
    bar: 123
};
複製代碼

因此說,類型斷言是不夠嚴謹的,建議使用var foo : Foo這種方式。

指定event類型

function handler (event: Event) {
    let mouseEvent = event as MouseEvent;
}
複製代碼
function handler(event: Event) {
    let element = event as HTMLElement; // HTMLElement不是一個徹底的event子類型,所以不能充分重疊,須要加一個unknown或者any
}
複製代碼

二次斷言編譯提示取消:

function handler(event: Event) {
    let element = event as unknown as HTMLElement; // Okay!
}
複製代碼

慎用as any和as unknown

一般狀況是類型斷言S和T的話,S爲T的子類型,或者T爲S的子類型,這種是相對安全的。 假如是用as any或者as unknown,是很是不安全的。慎用!慎用!

// 謹慎使用
as any
as known
複製代碼

type與類型斷言

type keys = 'foo' | 'bar' | 'baz'obj[key as keys]是什麼意思? 與variable:type相似,這是另一種類型約束。

若是不明白的花,看完下面這個demo就明白了。

type keys = 'foo' | 'bar' | 'baz'
const obj = {
    foo: 'a',
    bar: 'b',
    baz: 'c'
}
const test = (key:any) => {
    return obj[key] ; // 提示錯誤 type 'any' can't be used to index type '{ foo: string; bar: string; baz: string; }'.
}
複製代碼

如何解決這個報錯呢? 第一種方式:類型約束

const test = (key:keys) => {
    return obj[key] ;
}
複製代碼

第二種方式:類型斷言(這種方式經常使用於第三方庫的callback,返回值類型沒有約束的狀況)

const test = (key:any) => {
    return obj[key as keys] ;
}
複製代碼

須要注意:obj[key as keys]中keys的類型能夠少於obj的類型,反過來obj的屬性不能少於keys的類型。

泛型函數與泛型接口

泛型函數

想一想一個場景,咱們但願函數的輸入與輸出類型一致。 你可能會這樣作,但這並不能保障輸入與輸出類型一致。

function log(value: any):any {
    return value;
}
複製代碼

經過泛型函數能夠精準實現:函數名後加一個<T>這裏的T能夠理解爲泛型的名字。指定輸入類型爲T,返回值爲T。

function log<T>(value: T):T {
    return value;
}
複製代碼

這是一個泛型函數實例,如何定義一種泛型函數類型呢?

type Log = <T>(value: T) => T
複製代碼

使用泛型函數類型約束函數:

let log : Log = function <T>(value: T):T {
    return value;
}
複製代碼

泛型接口

接口全部屬性靈活,輸入輸出一致便可。

interface Log {
     <T>(value: T): T
}
let myLog: Log = log
myLog("s")// "s"
myLog(1)// 1
複製代碼

接口全部屬性必須爲同一類型。

interface Log<T> {
     (value: T): T
}
let myLog: Log<string> = log
myLog("s")// "s"
myLog(1)// Error
複製代碼

ts中的<>

在ts中,遇到<>的話,尖括號中間大多狀況下都是類型。

  • Array<string>
  • <string>[]
  • function <T>(value: T): T { ... }
  • type MyType = <T>(value : T) => T
  • interface MyInterface<T> { (value: T): T }

如何理解as const?

  • 爲了解決let賦值問題的,將一個mutable的變量改成readonly。
  • 避免將類型推斷爲聯合類型。

爲了解決let賦值問題的,將一個mutable的變量改成readonly。

let x = "hello";
x = "world"; // 報錯
複製代碼
第一種方式 const
const x = "hello"
複製代碼
第二種方式 "hello"類型
let x: "hello" = "hello";
x = "world"; // 
複製代碼
第三種方式 discriminated unions
type Shape =
    | { kind: "circle", radius: number }
    | { kind: "square", sideLength: number }
function getShapes(): readonly Shape[] {
    // to avoid widening in the first place.
    let result: readonly Shape[] = [
        { kind: "circle", radius: 100, },
        { kind: "square", sideLength: 50, },
    ];
    return result;
}
複製代碼
第四種方式 as const

.tsx類型文件

// Type '10'
let x = 10 as const;

// Type 'readonly [10, 20]'
let y = [10, 20] as const;

// Type '{ readonly text: "hello" }'
let z = { text: "hello" } as const;
複製代碼

非.tsx類型文件

// Type '10'
let x = <const>10;

// Type 'readonly [10, 20]'
let y = <const>[10, 20];

// Type '{ readonly text: "hello" }'
let z = <const>{ text: "hello" };
複製代碼

優化discriminated unions

function getShapes() {
    let result = [
        { kind: "circle", radius: 100, },
        { kind: "square", sideLength: 50, },
    ] as const;
    
    return result;
}

for (const shape of getShapes()) {
    // Narrows perfectly!
    if (shape.kind === "circle") {
        console.log("Circle radius", shape.radius);
    }
    else {
        console.log("Square side length", shape.sideLength);
    }
}
複製代碼

避免將類型推斷爲聯合類型。

避免將類型推斷爲 (boolean | typeof load)[],而是推斷爲[boolean, typeof load]。

export function useLoading() {
  const [isLoading, setState] = React.useState(false);
  const load = (aPromise: Promise<any>) => {
    setState(true);
    return aPromise.finally(() => setState(false));
  };
  return [isLoading, load] as const; // infers [boolean, typeof load] instead of (boolean | typeof load)[]
}
複製代碼

declare global是什麼意思?

是爲了在全局命名空間作聲明,好比爲對象增長一個未定義的屬性。

爲Window增長csrf的定義

declare global {
  interface Window {
    csrf: string;
  }
}
複製代碼

爲String增長fancyFormat的定義

declare global {
  /*~ Here, declare things that go in the global namespace, or augment *~ existing declarations in the global namespace */
  interface String {
    fancyFormat(opts: StringFormatOptions): string;
  }
}
複製代碼

注意global做用域只能用於導出模塊或者外部的模塊聲明

Augmentations for the global scope can only be directly nested in external modules or ambient module declarations.

如何在TypeScript環境增長一個全局變量?

好比咱們想要實現下面的效果,可是會報錯Property 'INITIAL_DATA' does not exist

<script>
  window.__INITIAL_DATA__ = {
    "userID": "536891193569405430"
  };
</script>
const initialData = window.__INITIAL_DATA__; // 報錯 
複製代碼

使用類型斷言

const initialData = (window as any).__INITIAL_DATA__;
複製代碼
type InitialData = {
  userID: string;
};

const initialData = (window as any).__INITIAL_DATA__ as InitialData;
const userID = initialData.userID; // Type string
複製代碼

聲明全局變量

declare var __INITIAL_DATA__: InitialData;
複製代碼
const initialData = __INITIAL_DATA__;
const initialData = window.__INITIAL_DATA__;
複製代碼

在es模塊中,有import,export的,須要這樣作:

export function someExportedFunction() {
  // ...
}

declare global {
   var __INITIAL_DATA__: InitialData;
}
const initialData = window.__INITIAL_DATA__;
複製代碼

若是在不少文件都用到的話,能夠用一個globals.d.ts文件。

利用interface合併

interface Window {
  __INITIAL_DATA__: InitialData;
}
const initialData = window.__INITIAL_DATA__;
複製代碼

在js模塊中須要像下面這樣:

export function someExportedFunction() {
  // ...
}

declare global {
  interface Window {
    __INITIAL_DATA__: InitialData;
  }
}

const initialData = window.__INITIAL_DATA__;
複製代碼

interface能夠繼承嗎?

能夠的。

interface Base {
    foo: string;
}

interface Props extends Base {
    bar: string
    baz?: string
}

const test = (props: Props) => {
    console.log(props);
}

test({ foo: 'hello' }) // Property 'bar' is missing in type '{ foo: string; }' but required in type 'Props'
test({ foo: 'hello', bar: 'world' })
複製代碼

當Props繼承了Base以後,實際上它最終變成了下面這樣:

interface Props extends Base {
    foo: string;
    bar: string
    baz?: string
}
複製代碼

Props能夠覆蓋Base嗎?能夠,可是隻能是required覆蓋optional,optional不能覆蓋required。

// ✅
interface Base {
    foo?: string;
}

interface Props extends Base {
    foo: string;
    bar: string
    baz?: string
}
複製代碼
// ❌
interface Base {
    foo: string;
}

interface Props extends Base {
    foo?: string;
    bar: string
    baz?: string
}
複製代碼

typescript中的&是什麼意思?

在react的dts文件中有這樣一個定義。

type PropsWithChildren<P> = P & { children?: ReactNode };
複製代碼

typescript中的&指的是交叉類型。

interface ErrorHandling {
  success: boolean;
  error?: { message: string };
}

interface ArtworksData {
  artworks: { title: string }[];
}

interface ArtistsData {
  artists: { name: string }[];
}

// These interfaces are composed to have
// consistent error handling, and their own data.

type ArtworksResponse = ArtworksData & ErrorHandling;
type ArtistsResponse = ArtistsData & ErrorHandling;

const handleArtistsResponse = (response: ArtistsResponse) => {
  if (response.error) {
    console.error(response.error.message);
    return;
  }

  console.log(response.artists);
};
複製代碼

知道&是ts中的交叉類型之後,咱們就明白PropsWithChildren的意思了,並且也明白爲何react的函數式組件會比普通函數組件多了children屬性。

它的意思是PropsWithChildren類型是P和對象{children?: ReactNode}的交叉類型,也就是經過&鏈接兩個對象以後,最終生成的對象是擁有children這個可選屬性的。

interface與type的區別是什麼?

An interface can be named in an extends or implements clause, but a type alias for an object type literal cannot. An interface can have multiple merged declarations, but a type alias for an object type literal cannot.

  1. interface能夠繼承(好比用extends),type不能夠
  2. interface能夠實現有多個合併聲明,type不能夠

enum做爲一種類型是什麼意思?

在閱讀pixi.js的源碼中,發現有將enum做爲了一種類型。

enum也能夠做爲一種類型去約束。

// pixi/constants
export enum BLEND_MODES {
    NORMAL = 0,
    ADD = 1,
    MULTIPLY = 2,
    SCREEN = 3,
    OVERLAY = 4,
}

export enum ANOTHER_ENUM {
    FOO = 5,
    BAR = 6
}

import { BLEND_MODES } from '@pixi/constants';

export class Sprite extends Container {
    public blendMode: BLEND_MODES;
    constructor(){
        this.blendMode = BLEND_MODES.NORMAL; // 最佳
        // this.blendMode = 0 這樣是能夠的,次之
        // this.blendMode = ANOTHER_ENUM.FOO 這樣ts會報錯
    }
}
複製代碼

項目中xxx.d.ts的declare module '*.scss'是什麼意思?declare module還能夠作什麼?

項目中xxx.d.ts的declare module '*.scss'是什麼意思?

// externals.d.ts
declare module '*.scss'
複製代碼

默認狀況下import style from 'style.scss'在ts的ide校驗器裏會報錯,那就用d.ts假定定義全部scss結尾的文件是module。--社長

假設將declare module '*.scss'註釋掉,ide會報錯,可是能夠經過lint。

declare module還能夠作什麼?

當咱們引入了一個微軟官方@types/*中不存在的自定義包時,ide會報錯。

例以下面這樣: image

如何解決這個報紅的錯誤呢?declare module

// typing.d.ts
declare module 'visual-array'
複製代碼

這樣報紅就消失了。

typescript如何約束Promise的類型?

Promise泛型函數

interface Promise<T> {
    then<TResult1 = T, TResult2 = never>(onfulfilled?: ((value: T) => TResult1 | PromiseLike<TResult1>) | undefined | null, onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | undefined | null): Promise<TResult1 | TResult2>;
    catch<TResult = never>(onrejected?: ((reason: any) => TResult | PromiseLike<TResult>) | undefined | null): Promise<T | TResult>;
}
複製代碼
interface foo {
   bar: ()=>Promise<string>,
   baz: ()=>Promise<number[]>,
   car: (id)=>Promise<boolean[]>
}
複製代碼

typescript中的keyof如何使用?

最簡

type Point = { x: number; y: number };
type P = keyof Point; // 'x' | 'y'
let foo: P = 'x';
let bar: P = 'y';
let baz: P = 'z'; // ❌
複製代碼

經常使用

interface Person {
  name: string;
  age: number;
  location: string;
}
type K1 = keyof Person; // "name" | "age" | "location"
type K2 = keyof Person[]; // "length" | "push" | "pop" | "concat" | ...
type K3 = keyof { [x: string]: Person }; // string

type P1 = Person["name"]; // string
type P2 = Person["name" | "age"]; // string | number
type P3 = string["charAt"]; // (pos: number) => string
type P4 = string[]["push"]; // (...items: string[]) => number
type P5 = string[][0]; // string
複製代碼

keyof使得函數類型安全(type-safe)

function getProperty<T, K extends keyof T>(obj: T, key: K) {
  return obj[key]; // Inferred type is T[K]
}
function setProperty<T, K extends keyof T>(obj: T, key: K, value: T[K]) {
  obj[key] = value;
}
let x = { foo: 10, bar: "hello!" };
let foo = getProperty(x, "foo"); // number
let bar = getProperty(x, "bar"); // string
let oops = getProperty(x, "wargarbl"); // Error! "wargarbl" is not "foo" | "bar"
setProperty(x, "foo", "string"); // Error!, string expected number
複製代碼

Partial,Required,Readonly,Pick 泛型工具類型的實現原理

type Partial<T> = {
    [P in keyof T]? : T[P];
}
複製代碼
type Required<T> = {
    [P in keyof T]?- : T[P]; 
}
複製代碼
type Readonly<T> = {
    readonly [P in keyof T] : T[P];
}
複製代碼
type Pick<T, K extends keyof T> = {
    [P in K]: T[P]
}
複製代碼

typescript中的typeof如何使用?

js中的typeof主要用於表達式上下文,而ts中的typeof主要用於類型上下文。

let s = "hello";
let n: typeof s;
// ^ = let n: string
複製代碼
type Predicate = (x: unknown) => boolean;
type K = ReturnType<Predicate>;
// ^ = type K = boolean
複製代碼
function f() {
  return { x: 10, y: 3 };
}
type P = ReturnType<typeof f>;
// ^ = type P = {
// x: number;
// y: number;
// }
複製代碼

typescript中的non-null assert operator是什麼?

非null斷言操做符:當爲null時,發生斷言,拋出異常。 可選鏈:當爲null/undefined時,返回undefined。

非空斷言操做符和可選鏈操做符測試

// Non-Null Assertion Operator

const obj = null;

interface Entity {
  name?: string;
}

// 非空斷言操做符
function nonNull(e?: Entity) {
  const s = e!.name; // 發生斷言,拋出TypeError
}

try {
  nonNull(obj);
} catch (e) {
  console.error("nonNull catch", e); // TypeError: Cannot read property 'name' of null
}

// 可選鏈
function optionalChaining(e?: Entity) {
  const s = e?.name;
  console.log(s); // undefined
}

optionalChaining(obj);
複製代碼

用於函數返回值空檢測

function returnNullFunc() {
  return null;
}

try {
  returnNullFunc()!.age;
} catch (e) {
  console.error("returnNullFunc", e); // TypeError: Cannot read property 'age' of null
}

function returnNonNullFunc() {
  return {
    age: "18"
  };
}
returnNonNullFunc()!.age;

複製代碼

在線demo:codesandbox.io/s/non-null-…

期待和你們交流,共同進步:

努力成爲優秀前端工程師!

相關文章
相關標籤/搜索