l-hammer / learning Goto Github PK
View Code? Open in Web Editor NEW记录学习过程
记录学习过程
在
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
基类(超类)。
访问 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 增加代理所有网站与端口。
TypeScript
的核心原则之一是对值所具有的结构进行类型检查。 它有时被称做“鸭式辨型法(会走路、游泳和呱呱叫的鸟就是鸭子)”或“结构性子类型化”。 在TypeScript
里,接口的作用就是为这些类型命名和为你的代码或第三方代码定义契约。
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);
可以看到,编译后接口部分并没有对应的代码。
接口里的属性并不总是必须的。我们可以通过在接口属性后面加一个 ”?“ 符号将其标记为可选属性。
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"});
如果我们不小心拼错属性名,检测器会给出如下提示:
上面的例子中,假如我们传入非接口SquareConfig
中定义的属性,会怎么样?
createSquare({ color: "black", opacity: 0.5 });
检测器会给出如下提示:
那么,我们如何绕过额外属性的检查呢?
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
来指定。
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[]; // ✔️
接口除了描述带有属性的普通对象外,也可以描述函数类型。
为了使用接口表示函数类型,我们需要给接口定义一个调用签名。它就像是一个只有参数列表和返回值类型的函数定义。参数列表里的每个参数都需要名字和类型。
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;
};
与描述函数类型差不多,接口也可以描述那些能够“通过索引得到”的类型,比如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
支持string
和number
两种索引签名。也可以同时支持两种签名,但是数字索引的返回值必须是字符串索引返回值的子类型。这是因为当使用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"; // ❌
同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
存在于类的静态部分,不在其检查范围内。
下面的例子中,我们定义了两个接口,ClockConstructor
和ClockInterface
。为了方便我们定义了一个构造函数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)
中会检查DigitalClock
和AnalogClock
是否符合构造函数签名。
另外一种的简单的方式是使用类表达式
const Clock: ClockConstructor = class Clock implements ClockInterface {
constructor(h: number, m: number) { };
tick() {
console.log("beep beep");
}
}
和类一样,接口也可以相互继承。这让我们能够从一个接口复制成员到另外一个接口,可以更灵活的将接口分割到可重用的的模块里。
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;
因为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
第三方库时,你可能需要像上面那样去完整的定义类型。
当接口继承一个类类型时,它会继承类的成员但不包括其实现。就好像接口声明了所有类中存在的成员,但并没有提供具体实现一样。接口同样会继承到类的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
的类是一样的。Button
和TextBox
类是SelectableControl
的子类(因为它们都继承至Control
并有select
方法),但ImageC
类并不是这样的。
Refs:
» Interfaces
const isDone: boolean = true;
const decimal: number = 18; // 十进制
const hex: number = 0x12; // 十六进制
const binary: number = 0b1010; // 二进制
const octal: number = 0o22; // 八进制
const color: string = "blue";
模板字符串
const name: string = `lhammer`;
const sentence: string = `Hello, my name is ${name}`;
const list: number[] = [1, 2, 3]; // 数字型数组
第二种方式是使用数组泛型:Array<元素类型>
const list: Array<number> = [1, 2, 3];
类似于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"; // ❌,越界元素不存在
枚举类型是对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
能兼容TypeScript
中所有的类型,同时也会跳过类型检测,一般用在我们无法确定变量类型的时候。
let notSure: any = 8;
notSure = "maybe a string instead"; // ✔️
notSure = false; // ✔️
let list: any[] = [1, true, "free"];
list[1] = 100; // ✔️
与 any
类型相反,void
表示没有类型,一般用在当一个函数没有返回值的时候。
function warnUser(): void {
console.log("This is my warning message");
}
通常情况我们可以把null
和 undefined
看成是所有类型的子类型,两者都可以赋值给任意类型的变量。
let u: undefined = undefined
let n: null = null
然而,当你设定了
--strictNullChecks
标记,null
和undefined
只能赋值给void
和它们各自,这能避免很多常见的问题。 也许在某处你想传入一个string
或null
或undefined
,你可以使用联合类型string | null | undefined
。
never
表示那些永远不会发生的类型,同时也是所有类型的子类型,可以赋值给任意类型的变量。
常用的几个例子:
const error: never = (() => {
throw new Error("error");
})();
const loop: never = (() => {
while (true) {}
})();
object
表示非原始类型,也就是除 number
,string
,boolean
,symbol
,null
或undefined
之外的类型。
使用 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 -- 枚举
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.