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'.
复制代码
相关文章
相关标签/搜索