GithubHelp home page GithubHelp logo

learning's People

Stargazers

 avatar

Watchers

 avatar  avatar

learning's Issues

TS 类(classes)

什么是类?

ECMAScript 2015规范中,引入了class的概念,让我们能够使用基于类的面向对象的模式。TypeScript中,我们可以直接使用这些特性。

先来看一个简单示例:

class Greeter {
  greeting: string;

  constructor(message: string) {
    this.greeting = message;
  }

  greet() {
    return `Hello ${this.greeting}`;
  }
}

const greeter = new Greeter("world");

示例中我们声明了一个Greeter类,这个类包含3个成员,一个greeting属性,一个构造函数和一个greet方法。最后我们通过new关键字构造了Greeter类的一个实例。

继承

TypeScript中,我们可以使用常用的面向对象模式。基于类的程序设计中一种最基本的模式就是允许使用继承来扩展现有的类。

示例:

class Animal {
  move(distance: number = 0) {
    console.log(`Animal moved ${distance}m.`);
  }
}

class Dog extends Animal {
  bark() {
    console.log("Woof! Woof!");
  }
}

const dog = new Dog();

dog.bark();
dog.move(10);

示例展示了最基本的继承。Dog是一个派生类(子类),通过extends关键字派生至Animal基类(超类)。

Charles 激活 & 证书

激活

访问 https://www.zzzmode.com/mytools/charles 生成charles.jar文件并下载,替换源文件/Applications/Charles.app/Contents/Java/charles.jar

证书

1、手机访问 chls.pro/ssl 下载证书;(xxx.crt)
2、打开设置 -> 更多设置 -> 系统安全 -> 加密与凭据 -> 从存储设备安装;(xiaomi miui 11.0.1)
3、Charles -> proxy -> SSL proxy settings 增加代理所有网站与端口。

image

TS 接口

什么是接口(Interface)?

TypeScript的核心原则之一是对值所具有的结构进行类型检查。 它有时被称做“鸭式辨型法(会走路、游泳和呱呱叫的鸟就是鸭子)”或“结构性子类型化”。 在TypeScript里,接口的作用就是为这些类型命名和为你的代码或第三方代码定义契约。

接口初探(Our First Interface)

interface People {
  name: string,
  age: number
}

function printName(people: People): void {
  console.log(people.name);
}

const p_1 = { name: "Jack", age: 26 };
const p_2 = { age: 28, name: "Evan" };

printName(p_1);
printName(p_2);

People接口就好比一个名字,用来描述上述例子接收参数的结构。和其它语言接口不同的是,接口并不是传给printName的对象实现的,我们只关注值的外形,只要传入的对象满足上述提到的必要条件,那么它就是匹配的。另外,类型检查器并不会去检测属性的顺序,只要属性存在并且类型对应即可。

编译后对应的JavaScript如下:

function printName(people) {
  console.log(people.name);
}
var p_1 = { name: "Jack", age: 26 };
var p_2 = { age: 28, name: "Evan" };
printName(p_1);
printName(p_2);

可以看到,编译后接口部分并没有对应的代码。

可选属性(Optional Properties)

接口里的属性并不总是必须的。我们可以通过在接口属性后面加一个 ”?“ 符号将其标记为可选属性。

interface Square {
  color: string,
  area: number
}

interface SquareConfig {
  color?: string,
  width?: number
}

function createSquare(config: SquareConfig): Square {
  let newSquare = { color: "white", area: 100 };

  if (config.color) {
    newSquare.color = config.color;
  }
  if (config.width) {
    newSquare.area = config.width * config.width;
  }

  return newSquare;
}

createSquare({color: "black"});

如果我们不小心拼错属性名,检测器会给出如下提示:

image

额外的属性检查(Excess Property Checks)

上面的例子中,假如我们传入非接口SquareConfig中定义的属性,会怎么样?

createSquare({ color: "black",  opacity: 0.5 });

检测器会给出如下提示:

image

那么,我们如何绕过额外属性的检查呢?

1、使用as类型断言:

createSquare({ color: "black",  opacity: 0.5 } as SquareConfig);

2、接口中添加一个字符串类型的索引签名,并将它的类型设置为any:

interface SquareConfig {
  color?: string,
  width?: number,

  [propName: string]: any
}

TIPS: 这里索引名称中的propName除了增加可读性外,并没有什么实际意义。

然而绕过这些检查可能会遗漏真正的错误。我们应该重新审查接口的定义。上面例子中,可以通过修改SquareConfig支持传入opacity属性来解决问题。

interface SquareConfig {
  color?: string,
  width?: number,
  opacity?: number
}

只读属性(Readonly properties)

一些对象的属性在创建后不允许修改,我们可以通过在属性名前用readonly来指定。

interface Point {
  readonly x: number;
  readonly y: number;
}

let point_1: Point = { x: 10, y: 20 };

point_1.x = 20; // ❌

TypeScript 提供了ReadonlyArray<T> 类型,我们可以通过它创建一个不允许修改的数组。

let a: number[] = [1, 2, 3, 4];
let ro: ReadonlyArray<number> = a;

ro[0] = 12; // ❌
ro.push(5); // ❌
ro.length = 100; // ❌
a = ro; // ❌

某些情况下,我们可以通过类型断言跳过这个限制(相信我,我知道自己在干什么)。

(ro as number[])[0] = 12; // ✔️
(ro as number[]).push(5); // ✔️
(ro as number[]).length = 100; // ✔️
a = ro as number[]; // ✔️

函数类型(Function Types)

接口除了描述带有属性的普通对象外,也可以描述函数类型。

为了使用接口表示函数类型,我们需要给接口定义一个调用签名。它就像是一个只有参数列表和返回值类型的函数定义。参数列表里的每个参数都需要名字和类型。

interface SearchFunc {
  (source: string, subString: string): boolean;
}

const mySearch: SearchFunc = function (source, subString) {
  const result = source.search(subString);

  return result > -1;
};

这里函数的参数名不需要和接口中定义的名称完全一致,检查器会逐个对参数进行检查,只需对应位置上的类型匹配就可以。

const mySearch: SearchFunc = function (src, sub) {
  const result = src.search(sub);

  return result > -1;
};

可索引的类型(Indexable Types)

与描述函数类型差不多,接口也可以描述那些能够“通过索引得到”的类型,比如a[10] or ageMap["daniel"]。可索引类型具有一个索引签名,用来描述对象索引的类型以及索引相对应的返回值类型。例如:

interface StringArray {
  [index: number]: string
}

let myArray: StringArray = ["Bob", "Fred"];

let myStr: string = myArray[0];

上述示例中,我们定义了StringArray接口,它具有一个索引签名。这个索引签名描述了当用number去索引StringArray时会得到一个string类型的返回值。

当你声明一个索引签名时,所有明确的成员都必须符合索引签名:

interface Foo {
  [key: string]: number,
  x: number,
  y: number
}

interface Bar {
  [key: string]: number,
  x: number,
  y: string // ❌
}

TypeScript支持stringnumber两种索引签名。也可以同时支持两种签名,但是数字索引的返回值必须是字符串索引返回值的子类型。这是因为当使用number来索引时,JavaScript会将它转换成string(调用toString方法)然后再去索引对象。

class Animal {
  name: string;
}

class Dog extends Animal {
  bark() {
    console.log("Woof! Woof!");
  }
}

interface NotOkay {
  [x: number]: Animal, // ❌ 数字索引类型“Animal”不能赋给字符串索引类型“Dog”。
  [y: string]: Dog
}

interface Okay {
  [x: number]: Dog,
  [y: string]: Animal
}

同其它属性一样,索引签名也支持设置为只读。

interface ReadonlyStringArray {
  readonly [index: number]: string
}

const arr: ReadonlyStringArray = ["Tom", "Bob"];

arr[1] = "Ali"; // ❌

类类型(Class Types)

C#Java里接口的基本作用一样,TypeScript也能够用它来明确的强制一个类去符合某种契约。

interface ClockInterface {
  currentTime: Date
}

class Clock implements ClockInterface {
  currentTime: Date;
  constructor(h: number, m: number) { }
}

我们也可以在接口中描述一个方法,在类中实现它。

interface ClockInterface {
  currentTime: Date;

  setTime(d: Date);
}

class Clock implements ClockInterface {
  currentTime: Date;

  constructor(h: number, m: number) { };

  setTime(d: Date) {
    this.currentTime = d;
  }
}

注意接口只描述了类的公共部分,并不包含私有部分。

当你用构造器签名去定义一个接口并试图通过类去实现这个接口时会得到一个错误:

interface ClockInterface {
  currentTime: Date;

  new (hour: number, mimute: number);
}

class Clock implements ClockInterface {
  currentTime: Date;

  constructor(h: number, m: number) { }; // ❌ 类型“Clock”提供的内容与签名“new (hour: number, mimute: number): any”不匹配。
}

这是因为当一个类实现一个接口时,只对其实例部分进行了类型检查。constructor存在于类的静态部分,不在其检查范围内。

下面的例子中,我们定义了两个接口,ClockConstructorClockInterface。为了方便我们定义了一个构造函数createClock,它通过传入的类型创建实例。

interface ClockConstructor {
  new (hour: number, mimute: number)
}

interface ClockInterface {
  tick(): void;
}

function createClock(
  ctor: ClockConstructor,
  hour: number,
  minute: number
): ClockInterface {
  return new ctor(hour, minute);
}

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

const digital = createClock(DigitalClock, 17, 20);
const analog = createClock(AnalogClock, 17, 21);

因为createClock的第一个参数是ClockConstructor类型,在createClock(DigitalClock, 17, 20)中会检查DigitalClockAnalogClock是否符合构造函数签名。

另外一种的简单的方式是使用类表达式

const Clock: ClockConstructor = class Clock implements ClockInterface {
  constructor(h: number, m: number) { };

  tick() {
    console.log("beep beep");
  }
}

继承接口(Extending Interfaces)

和类一样,接口也可以相互继承。这让我们能够从一个接口复制成员到另外一个接口,可以更灵活的将接口分割到可重用的的模块里。

interface Shape {
  color: string
}

interface Square extends Shape {
  sideLength: number
}

let square = {} as Square;

square.color = "blue";
square.sideLength = 10;

一个接口也可以同时继承多个接口。

interface Shape {
  color: string
}

interface PenStroke {
  penWidth: number
}

interface Square extends Shape, PenStroke {
  sideLength: number
}

let square = {} as Square;

square.color = "blue";
square.sideLength = 10;
square.penWidth = 5.0;

混合类型(Hybrid Types)

因为JavaScript其动态灵活的特点,有时你会希望一个对象可以同时具有上面提到的多种类型。

常见的一个例子就是,一个对象可以同时做为函数和对象使用。

interface Counter {
  (start: number): string;
  interval: number;
  reset(): void
}

function getCounter(): Counter {
  let counter = (function (start) {}) as Counter;

  counter.interval = 123;
  counter.reset = function () {};

  return counter;
}

let c = getCounter();

c(10);
c.interval = 456;
c.reset();

在使用JavaScript第三方库时,你可能需要像上面那样去完整的定义类型。

接口继承类(Interfaces Extending Classes)

当接口继承一个类类型时,它会继承类的成员但不包括其实现。就好像接口声明了所有类中存在的成员,但并没有提供具体实现一样。接口同样会继承到类的private和protected 成员。这意味着当你创建了一个接口继承了拥有私有或受保护的成员的类时,这个接口类型只能被这个类或其子类所实现。

当你有一个庞大的继承结构时这很有用,但要指出的是你的代码只在子类拥有特定属性时起作用。这个子类除了继承至基类外与基类没有任何关系。例如:

class Control {
  private state: boolean
}

interface SelectableControl extends Control {
  select(): void
}

class Button extends Control implements SelectableControl {
  select() {}
}

class TextBox extends Control {
  select() {}
}

// ❌ Property 'state' is missing in type 'ImageC' but required in type 'SelectableControl'.ts(2420)
class ImageC implements SelectableControl {
  select() {}
}

上面例子中,SelectableControl包含了Control的所有成员,包括私有成员state。因为state是私有成员,所以只能够是Control的子类才能实现SelectableControl接口。因为只有Control的子类才能够声明Control的私有成员state,这对私有成员的兼容性是必须的。

Control类内部,是允许通过SelectableControl的实例来访问私有成员state的。实际上,SelectableControl接口和拥有select方法的Control的类是一样的。ButtonTextBox类是SelectableControl的子类(因为它们都继承至Control并有select方法),但ImageC类并不是这样的。


Refs:
» Interfaces

TS 基础类型

布尔(Boolean)

const isDone: boolean = true;

数字(Number)

const decimal: number = 18; // 十进制
const hex: number = 0x12; // 十六进制
const binary: number = 0b1010; // 二进制
const octal: number = 0o22; // 八进制

字符串(String)

const color: string = "blue";

模板字符串

const name: string = `lhammer`;
const sentence: string = `Hello, my name is ${name}`;

数组(Array)

const list: number[] = [1, 2, 3]; // 数字型数组

第二种方式是使用数组泛型:Array<元素类型>

const list: Array<number> = [1, 2, 3];

元祖(Tuple)

类似于JavaScript 中的数组,元祖可以包含任何类型的成员。

let x: [string, number];

x = ["hello", 10]; // ✔️
x = [10, "hello"]; // ❌

x[0].slice(1); // ✔️
x[1].slice(1); // ❌,"number" 不存在`slice`方法

x[1] = true; // ❌,"true" 不能分配给类型 "number"
x[2] = "world"; // ❌,越界元素不存在

枚举(Enum)

枚举类型是对JavaScript 数据类型的一个补充,使用枚举类型可以为一组数值赋予友好的名字。

数字类型枚举

enum Color {
  Red,
  Green = 2,
  Blue
}

let c1: Color = Color.Red; // 0
let c2: Color = Color.Green; // 2
let c3: Color = Color.Blue; // 3

枚举值默认是从 0 开始的索引值,也可手动指定。

其编译后对应的JavaScript如下:

var Color;
(function (Color) {
    Color[Color["Red"] = 0] = "Red";
    Color[Color["Green"] = 2] = "Green";
    Color[Color["Blue"] = 3] = "Blue";
})(Color || (Color = {}));

所以我们可以通过枚举值快速反查到对应的名称,如下所示:

let c3_key: string = Color[3]; // Blue

字符串枚举

enum ErrorTypes {
  ERROR_310 = "用户未登录",
  ERROR_404 = "请求不存在",
  ERROR_502 = "网关异常",
}
const resps = [{ code: 310}, { code: 404}, { code: 502}];

resps.forEach(resp => console.log(ErrorTypes[`ERROR_${resp.code}`]))

常量枚举

enum Weekday {
  Monday,
  Tuseday,
  Wednesday,
  Thursday,
  Friday,
  Saturday,
  Sunday
}

结合namespace向枚举类型添加静态方法

namespace Weekday {
  export function isBusinessDay(day: Weekday): Boolean {
    switch (day) {
      case Weekday.Saturday:
      case Weekday.Sunday:
        return false;
      default:
        return true;
    }
  }
}

const { Monday, Sunday } = Weekday;

Weekday.isBusinessDay(Monday); // true
Weekday.isBusinessDay(Sunday); // false

Any

any能兼容TypeScript中所有的类型,同时也会跳过类型检测,一般用在我们无法确定变量类型的时候。

let notSure: any = 8;

notSure = "maybe a string instead"; // ✔️
notSure = false; // ✔️

let list: any[] = [1, true, "free"];

list[1] = 100; // ✔️

Void

any 类型相反,void 表示没有类型,一般用在当一个函数没有返回值的时候。

function warnUser(): void {
  console.log("This is my warning message");
}

Null and Undefined

通常情况我们可以把nullundefined 看成是所有类型的子类型,两者都可以赋值给任意类型的变量。

let u: undefined = undefined
let n: null = null

然而,当你设定了 --strictNullChecks 标记,nullundefined 只能赋值给 void 和它们各自,这能避免很多常见的问题。 也许在某处你想传入一个 stringnullundefined,你可以使用联合类型 string | null | undefined

Never

never 表示那些永远不会发生的类型,同时也是所有类型的子类型,可以赋值给任意类型的变量。

常用的几个例子:

const error: never = (() => {
  throw new Error("error");
})();

const loop: never = (() => {
  while (true) {}
})();

Object

object 表示非原始类型,也就是除 numberstringbooleansymbolnullundefined 之外的类型。

使用 object 类型,就可以更好的表示像 Object.create 这样的 API。例如:

declare function create(o: object | null): void;

create({ prop:0 }); // ✔️
create(null); // ✔️

create(42); // ❌
create('string'); // ❌
create(false); // ❌

Refs:
» Basic Types
» 深入理解 TypeScript -- 枚举

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.