TS小冊 - 類型系統

寫在前面

本系列是博主在學習 TS 過程當中 閱讀以下文檔作出的筆記 若是有錯誤 但願在評論區指出哦 🤩🤩🤩javascript

TypeScript 中文手冊css

TypeScript 入門教程html

深刻理解 TypeScriptjava

TypeScript 官網python

babelreact

預計將會更新以下內容git

  • TS 小冊 - 類型系統 ✅ ✅github

  • TS 小冊 - 高級類型typescript

  • TS 小冊 - 模塊shell

  • TS 小冊 - 工具函數

  • TS 小冊 - JSX 和 React

...... (TODO) 好吧 我也不知道 最後會是怎樣 😛😛😛

類型系統

本文涉及的代碼 github.com/LuckyChou71…

基本數據類型

// boolean
let isDone: boolean;

// number
let count: number;

// string
let username: string;

// symbol
let unique: symbol;

// bigint
let bigBigBigNum: bigint;

/** * 默認狀況下null和undefined是全部類型的子類型 * 就是說你能夠把null和undefined賦值給number類型的變量 * 指定了--strictNullChecks標記,null和undefined只能賦值給void和它們各自 */

// undefined
let u: undefined;

// null
let n: null;

/** * never類型是任何類型的子類型,也能夠賦值給任何類型 * 然而,沒有類型是never的子類型或能夠賦值給never類型(除了never自己以外) * 即便any也不能夠賦值給never */

function error(message: string): never {
  throw new Error(message);
}
複製代碼

數組

// 數組
let arr1: number[];

// 數組泛型
let arr2: Array<number>;

// 元組 能夠限制元素的類型和個數
let arr3: [number, string];

// 最使人熟知的可能就是React的useState 它返回了一個元組
const [state, useState] = React.useState(null);
複製代碼

函數

// 爲函數定義類型
// 不能使用interface 由於interface只能定義對象
type Add = (x: number, y: number) => number;

function add1(x: number, y: number): number {
  return x + y;
}

const add2: Add = (x: number, y: number): number => {
  return x + y;
};

// 剩餘參數
function add3(x: number, ...y: Array<number>) {
  return x + y.reduce((x, y) => x + y);
}

// 默認參數
// 若是你想要默認參數生效的話 就把默認參數放在參數序列的最後一項吧
// 以下定義 就會形成歧義 編譯器不知道傳入一個參數的時候 到底想表達的是哪個值
// 最終 咱們若是想避免程序報錯的話 就不得不乖乖的傳入第二個參數 這樣咱們的默認參數就失效了
function add4(x: number = 2, y: number) {
  return x + y;
}
add4(3, 5);

// 可選參數
// 可選參數必須放在參數序列的最後一項
function add5(x: number, y?: number) {
  return x + y;
}

// 函數重載
function showType(x: any): any {
  if (typeof x === 'number') {
    return 'number';
  } else if (typeof x === 'string') {
    return 'string';
  } else {
    return "I don't know";
  }
}
複製代碼

枚舉

/** * 默認狀況下,從0開始爲元素編號 * 也能夠手動爲某個枚舉值賦值 下一個枚舉值爲前一個枚舉值 + 1 */

enum Color {
  Red,
  Green,
  Blue,
}

Color.Blue; // -->2

enum Count {
  one = 1,
  two,
  three,
}

Count.three; // --> 3

// 減小魔法數字
enum STATUS {
  READY = 0,
  OK = 1
  FAILED = -1
}

// 字符串枚舉
enum Lang {
  js = 'javascript',
  ts = 'typescript',
  jsx = 'react',
  py = 'python',
}
複製代碼

若是你感興趣它是如何實現的話 你能夠在 babel 官網去轉換它們

大體它們被 babel 轉換成以下

'use strict';

var Color;

(function (Color) {
  Color[(Color['Red'] = 0)] = 'Red';
  Color[(Color['Green'] = 1)] = 'Green';
  Color[(Color['Blue'] = 2)] = 'Blue';
})(Color || (Color = {}));

var Count;

(function (Count) {
  Count[(Count['one'] = 1)] = 'one';
  Count[(Count['two'] = 2)] = 'two';
  Count[(Count['three'] = 3)] = 'three';
})(Count || (Count = {}));

var Lang;

(function (Lang) {
  Lang['js'] = 'javascript';
  Lang['ts'] = 'typescript';
  Lang['jsx'] = 'react';
  Lang['py'] = 'python';
})(Lang || (Lang = {}));
複製代碼

// 類繼承接口
interface IPerson {
  readonly name: string;
}

// Person類中須要定義全部IPerson接口中的成員
class Person implements IPerson {
  // 公開 和不加修飾符 效果同樣 表示在任意位置均可以訪問
  public nickname: string;
  // 只讀 只能讀取 不能賦值
  readonly name: string = '花匠';
  // 受保護的 protected成員在派生類中仍然能夠訪問
  protected gender: string;
  // 私有 不能在聲明它的類的外部訪問
  private _age: number;
  constructor(nickname: string, gender: string, age: number) {
    this.nickname = nickname;
    this.gender = gender;
    this._age = age;
  }
  // 靜態方法 使用類型.調用
  static sayHi() {
    console.log(this.name);
  }
  // getters/setters 存取器件
  set age(age: number) {
    this._age = age;
  }
  get age(): number {
    return this._age;
  }
}

const person = new Person('nanshu', 'man', 18);
person.name; // --> 花匠
person.nickname; // --> nanshu

// 不能訪問gender 由於gender是protected 只能在聲明的類和派生類中訪問
// person.gender; Property 'gender' is protected and only accessible within class 'Person' and its subclasses.

// 不能訪問gender 由於age是private 只能在聲明的類中訪問
// stu.age; Property 'age' is private and only accessible within class 'Student'

class Student extends Person {
  constructor(nickname: string, gender: string, age: number) {
    super(nickname, gender, age);
    // 不能訪問父類中的私有成員
    // super.age; Property 'age' is private and only accessible within class 'Person'.
    
    // public protected readonly修飾的成員均可以訪問
    super.name;
    super.nickname;
    super.gender;
  }
}

// 和接口不一樣 接口不能包含成員的實現細節 且不能包含修飾符
// 可是抽象類能夠包含成員的實現細節 而且能夠包含訪問修飾符
abstract class Animal {
  constructor(public name: string) {
    this.name = name;
  }
  abstract makeSound(): void;
  move(): void {
    console.log('moving...');
  }
}

class Dog extends Animal {
  makeSound() {
    console.log('make sound');
  }
}

const dog = new Dog('小花');
dog.name; // --> 小花
dog.move(); // --> moving
dog.move(); // --> make sound

// 類看成接口使用
class Point {
  x: number;
  y: number;
}
const pointA: Point = { x: 7, y: 10 };

interface Point3D extends Point {
  z: number;
}

const pointB: Point3D = { x: 7, y: 1, z: 10 };
複製代碼

泛型

// 泛型函數
// 若是咱們想實現一個函數 相似shell中的echo 輸入什麼就返回什麼
// 可是 咱們不知道用戶在實際使用的時候 會傳入什麼類型 固然咱們可使用any 可是這🧐好像很隨意
// 或者咱們可使用函數重載 爲每個類型具體定義 這好像又🤨
// 這個時候咱們就可使用泛型 
function echo<T>(arg: T): T {
  return arg;
}

// 泛型類
class GenericNumber<T> {
  zeroValue: T;
  add: (x: T, y: T) => T;
}

// 泛型約束
// 由於編譯器不知道泛型 T 中擁有哪些方法 因此以下調用會會被ts警告⚠️
function loggingIdentity1<T>(arg: T): T {
  // console.log(arg.length); Property 'length' does not exist on type 'T'.
  return arg;
}

// 咱們能夠定義一個接口 讓泛型繼承這個接口 這樣咱們就能夠安全的使用lenght屬性了
interface Lengthwise {
  length: number;
}

function loggingIdentity2<T extends Lengthwise>(arg: T): T {
  // 這個時候就不會被ts警告⚠️了
  console.log(arg.length);
  return arg;
}
複製代碼

接口

/** * 沒法聲明基本數據類型 只能聲明對象 * 最簡單判斷該用readonly仍是const的方法是看要把它作爲變量使用仍是作爲一個屬性。 * 作爲變量使用的話用const,若作爲屬性則使用readonly */

interface IPerson {
  name: string;
  height?: number;
  readonly age: number;
}

// 多個interface會合並
interface IPerson {
  weight: number;
}

const person: IPerson = {
  name: 'nanshu',
  height: 181,
  weight: 140,
  age: 18,
};

// 索引簽名

// 表示這個對象接受 任意string的鍵 any的值
interface LooseObj {
  [k: string]: any;
}

// 你也能夠設置這個索引簽名爲只讀
interface ReadOnlyLooseObj {
  readonly [k: string]: any;
}

interface LooseArr {
  [k: number]: any;
}

const arr: LooseArr = [1, '1', true];
複製代碼

類型別名

// 類型別名用來給類型起一個新的名字
// 起別名不會新建一個類型 --> 它建立了一個新名字來引用那個類型
type Name = string;
type NameResolver = () => string;

const nickname: Name = 'nanshu';
const handleNameResolver: NameResolver = () => 'nanshu';

// 不一樣與interface type不能重複命名 可是type能夠聲明基本數據類型
// type Name = number; Duplicate identifier 'Name'.
複製代碼
相關文章
相關標籤/搜索