-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
TypeScript从0到1,进行中... #138
Comments
typescript深入浅出 笔记为什么要学习 typescript
TS开发者的四个层级
typescript 优势
章节1{
"compilerOptions": {
"target": "es5", // 指定 ECMAScript 目标版本: 'ES5'
"module": "commonjs", // 指定使用模块: 'commonjs', 'amd', 'system', 'umd' or 'es2015'
"moduleResolution": "node", // 选择模块解析策略
"experimentalDecorators": true, // 启用实验性的ES装饰器
"allowSyntheticDefaultImports": true, // 允许从没有设置默认导出的模块中默认导入。
"sourceMap": true, // 把 ts 文件编译成 js 文件的时候,同时生成对应的 map 文件
"strict": true, // 启用所有严格类型检查选项
"noImplicitAny": true, // 在表达式和声明上有隐含的 any类型时报错
"alwaysStrict": true, // 以严格模式检查模块,并在每个文件里加入 'use strict'
"declaration": true, // 生成相应的.d.ts文件
"removeComments": true, // 删除编译后的所有的注释
"noImplicitReturns": true, // 不是函数的所有返回路径都有返回值时报错
"importHelpers": true, // 从 tslib 导入辅助工具函数
"lib": ["es6", "dom"], // 指定要包含在编译中的库文件
"typeRoots": ["node_modules/@types"],
"outDir": "./dist",
"rootDir": "./src"
},
"include": [ // 需要编译的ts文件一个*表示文件匹配**表示忽略文件的深度问题
"./src/**/*.ts"
],
"exclude": [
"node_modules",
"dist",
"**/*.test.ts",
]
} 章节2-Typescript 的原始类型
章节3-Typescript 中其他常见类型
let obj: unknown;
obj.a.b; // error
obj(); // error
new obj(); // error
value[0][1]; // error
function throwError(message: string): never {
throw new Error(message);
}
const emptyArr: never[] = [];
章节4-深入理解枚举类型
enum Direction {
up, // 可指定初始值 up = 10;
down,
left,
right
}
console.log(Direction.up); // 0
console.log(Direction.down); // 1
console.log(Direction.left); // 2
console.log(Direction.right); // 3
enum Direction {
Up = 'Up',
Down = 'Down',
Left = 'Left',
Right = 'Right'
}
console.log(Direction['Right'], Direction.Up); // Right Up
enum BooleanLikeHeterogeneousEnum {
No = 0,
Yes = "YES",
}
emum Direction {
Up,
Down,
Left,
Right
}
console.log(Direction.Up === 0); // true
console.log(Direction.Down === 1); // true
console.log(Direction.Left === 2); // true
console.log(Direction.Right === 3); // true
console.log(Direction[0] === up); // true
const enum Direction {
Up = 'Up',
Down = 'Down',
Left = 'Left',
Right = 'Right'
}
const a = Direction.Up; // 如果要 typescript 保留对象 Direction,那么可以添加编译选项 --preserveConstEnums
var a = 'Up';
章节5-接口
章节6-类
章节7-函数 // 可选参数
const add = (a:number, b?: number) => a+b
// 剩余参数
const add1 = (a:number, ...rest: number[]) => rest.reduce((a,b) => a+b, a)
// 函数重载
interface Direction {
top: number,
bottom?: number,
left?: number,
right?: number
}
function assigned(all: number): Direction
function assigned(topAndBottom: number, leftAndRight: number): Direction
function assigned(top: number, right: number, bottom: number, left: number): Direction
function assigned (a: number, b?: number, c?: number, d?: number) {
if (b === undefined && c === undefined && d === undefined) {
b = c = d = a
} else if (c === undefined && d === undefined) {
c = a
d = b
}
return {
top: a,
right: b,
bottom: c,
left: d
}
}
assigned(1)
assigned(1,2)
assigned(1,2,3)
assigned(1,2,3,4) 章节8-泛型的妙用 // 单个类型
function returnItem<T>(para: T): T {
return para
}
// 多个类型
function swap<T, U>(tuple: [T, U]): [U, T] {
return [tuple[1], tuple[0]]
}
swap([7, 'seven'])
// 类型变量
function getArrayLength<T>(arg: Array<T>) {
console.log((arg as Array<any>).length) // ok
return arg
}
// 泛型接口
interface ReturnItemFn<T> {
(para: T): T
}
const returnItem: ReturnItemFn<number> = para => para // 泛型类
class Stack {
private arr: number[]: []
public push(item: number) {
this.arr.push(item)
}
public pop(){
this.arr.pop()
}
}
class Stack<T> {
private arr: T[]: []
public push(item: T) {
this.arr.push(item)
}
public pop() {
this.arr.pop()
}
}
// 泛型约束
type Params = number | string
class Stack<T extends Params> {
private arr: T[] = []
public push(item: T) {
this.arr.push(item)
}
public pop() {
this.arr.pop()
}
} // 函数接受两个参数,一个参数为对象,另一个参数为对象上的属性,返回属性值
function getValue(obj: object, key: string) {
return obj[key] // error
}
// 上面报错,需要借助 keyof T 将对象的属性类型取出生成一个联合类型
function getValue<T extends object, U extends keyof T>(obj: T, key: U) {
return obj[key]
}
// 多重类型约束
interface ChildInterface extends FirstInterface, SecondInterface {}
class Demo<T extends ChildInterface>
// 交叉类型多重约束
class Demo<T extedns FirstInterface & SecondInterface> // 参数 type 的类型 {new(): T} 表示泛型T是可被构造的,在被实例化后的类型是泛型T
function factory<T>(type: {new(): T}): T {
return new type()
} 章节9-类型断言与类型守卫 // 类型断言
const person = {}
person.name = 'a' // Error: name 属性不存在与{}
person.age = 20 // Error: age 属性不存在于{}
interface Person {
name: string;
age: number;
}
const person = {} as Person
person.name = 'a'
person.age = 20
// 双重断言
const user = 'b' as any as Person; // 类型守卫
instanceof 类型保护是通过构造函数来细化类型的一种方式
class Person {
name = 'a';
age = 20;
}
class Animal {
name = 'b';
color = 'red'
}
function getSometing(arg: Person | Animal) {
// 类型细化
if (arg instanceof Person) {
console.log(arg.color); // error 因为 arg 被细化为person 而person上不存在color属性
console.log(arg.age);
}
if (arg instanceof Animal) {
console.log(arg.age); // error
}
} // in: x in y 标识x属性存在于y中
class Person {
name = 'a';
age = 20;
}
class Animal {
name = 'b';
color = 'red'
}
function getSometing(arg: Person | Animal) {
if ('age' in arg) {
console.log(arg.color); // error
}
if ('color' in arg) {
console.log(arg.age); // error
}
} // 字面量类型守卫
type Foo = {
name: 'foo';
foo: number
}
type Bar = {
kind: 'bar';
bar: number;
}
function doStuff(arg: Foo | Bar) {
if (arg.kind === 'foo') {
console.log(arg.foo);
console.log(arg.bar); // error
} else {
console.log(arg.foo); // error
console.log(arg.bar);
}
} 章节10-类型兼容性类型兼容性用于确定一个类型是否能赋值给其他类型 结构类型class Person {
constructor(public weight: number, public name: string, public born: string) {
}
}
interface Dog {
name: string;
weight: number;
}
let x: Dog
x = new Person(120, 'a', '2021-01-01'); // person 包含 dog类所需的属性,所以不会报错 函数的类型兼容性// 函数参数兼容性
let x = (a: number) => 0;
let y = (b: number, s: string) => 0;
y = x; // ok
x = y; // Type '(b: number, s: string) => number' is not assignable to type '(a: number) => number'. // 当我们把 strictNullChecks 设置为 false 时代码是兼容的。
let foo = (x: number, y: number) => {}
let bar = (x?: number, y?:number) => {}
let bas = (...args: number[]) => {}
foo = bar = bas;
bas = bar = foo; let foo = (x: number, y: number) => {}
let bar = (x?: number) => {}
foo = bar // ok
bar = foo // Type '(x: number, y: number) => void' is not assignable to type '(x?: number) => void'. 枚举的类型兼容性 enum Status {
Ready,
Waiting
}
let status = Status.Ready;
let num = 0;
status = num;
num = status; 类的类型兼容性 // 仅仅只有实例成员和方法会相比较,构造函数和静态成员不会被检查
class Animal {
feet: number;
constructor(name: string, numFeet: number) {}
}
class Size {
feet: number;
constructor(meters: number) {}
}
let a: Animal;
let s: Size;
a = s;
s = a; // 私有的和受保护的成员必须来自相同的类
class Animal {
protected feet: number;
}
class Cat extends Animal {}
let animal: Animal;
let cat: Cat;
animal = cat;
cat = animal;
class Size {
protected feet: number;
}
let size: Size;
animal = size; // error
size = animal; // error 泛型的类型兼容性 // 泛型本身就是不确定的类型,它的表现根据是否被成员使用而不同
interface Person<T> {}
let x: Person<string>
let y: Person<number>
x = y // ok
y = x // ok interface Person<T>{
name: T
}
let x: Person<string>
let y: Person<number>
x = y;
y = x; // Type 'Person<number>' is not assignable to type 'Person<string>'.Type 'number' is not assignable to type 'string' interface Person {
name: string;
age: number;
weight: number;
}
interface Animal {
name: string;
age: number;
weight: number;
}
function getPersonName(p: Person) {
// 如何在传入animal的时候报错
} 章节11-高级类型:交叉类型、联合类型、类型别名交叉类型交叉类型是将多个类型合并为一个类型。折让我们可以把现有的多种类型叠加到一起成为一种类型,它包含了所需的所有类型的特性 interface IAnyObject {
[prop: string]: any
}
function mixin<T extends IAnyObject, U extends IAnyObject>(first: T, second: U): T & U {
const result = <T & U>{};
for(let id in first) {
(<T>result)[id] = first[id]
}
for(let id in second) {
if (!result.hasOwnProperty(id)) {
(<U>result)[id] = second[id];
}
}
return result;
}
const x = mixin({a: 'hello'}, {b: 42});
const a = x.a;
const b = x.b; 联合类型 // 联合类型:我们用竖线分隔没个类型
function formatCommandline(command: string[] | string) {
let line = '';
if (typeof command === 'string') {
line = command.trim()
} else {
line = command.join('').trim();
}
} 类型别名类型别名会给一个类型起个新名字,类型别名有时和接口很像,但是可以作用于原始值、联合类型、元组以及其他任何你需要手写的类型 type some = boolean | string;
const b: some = true // ok
const c: some = 'hello' // ok
const d: some = 123 // 不能将类型“123”分配给类型“some”
// 类型别名:泛型
type Container<T> = { value: T };
// 类型别名:可以再属性里引用自己
type Tree<T> = {
value: T;
left: Tree<T>;
right: Tree<T>;
} 类型别名和interface很像,区别是:
type Alias = { num: number }
interface Interface {
num: number
}
declare function aliased(arg: Alias): Alias
declare function interfaced(arg: Interface): interface; 章节12-可辨识联合类型前置概念:字面量类型 const a: 2333 = 2333 // ok
const ab : 0b10 = 2 // ok
const ao : 0o114 = 0b1001100 // ok
const ax : 0x514 = 0x514 // ok
const b : 0x1919n = 6425n // BigInt literals are not available when targeting lower than ES2020
const c : 'xiaomuzhu' = 'xiaomuzhu' // ok
const d : false = false // ok
const g: 'github' = 'pronhub' // Type '"pronhub"' is not assignable to type '"github"' 单个类型的字面量类型用途: type Direction = 'North' | 'East' | 'South' | 'West';
function move(distance: number, direction: Direction) {
// ...
} 类型字面量 type Foo = {
baz: [
number,
'xiaomuzhu'
];
toString(): string;
readonly [Symbol.iterator]: 'github';
0x1: 'foo';
'bar': 12n;
} 可辨识联合类型 interface Info {
username: string
}
interface UserAction {
id?: number
action: 'create' | 'delete'
info: Info
}
// 在创建的时候id是不需要传的,但是下面的结构确实合法的
const action: UserAction = {
action: 'create',
id: 111,
info: {
username: 'xiaomuzhu'
}
} // 这时需要 使用类型字面量 |
type UserAction = | {
id: number
action: 'delete'
info: Info
} | {
action: 'create'
info: Info
} 章节13-装饰器装饰器的主要作用:给一个已有的方法或类型扩展一些新的行为,而不是取直接修改它本身 装饰器目前扔处于 草案阶段,需要我们在 javascript中安装 babel插件:babel-plugin-transform-decorators-legacy 来支持 decorator,而在 Typescript中我们需要在 tsconfig.json 里面开启支持选项 experomentalDecorators
class Person {
say(){
console.log('hello')
}
}
function Person(){}
Object.defineProperty(Person.prototype, 'say', {
value: function(){ console.log('hello') },
enumerable: false,
configurable: true,
writable: true
}) 类装饰器 function addAge(constructor: Function) {
constructor.prototype.age = 18;
}
@addAge
class Person {
name: string;
age!: number;
constructor(){
this.name = 'xiaomuzhu'
}
}
let person = new Person();
console.log(person.age); // 18 属性、方法装饰器 function method(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
consle.log(target);
console.log('prop' + propertyKey)
console.log('desc' + JSON.stringify(descriptor))
descriptor.writable = false;
}
class Person {
name: string;
constructor(){
this.name = 'xiaomuzhu'
}
}
@method
say(){
return 'instance method'
}
@method
static run(){
return 'static method'
}
const xmz = new Person()
xmz.say = function(){
return 'edit'
}
console.log(xmz.say()) 章节-14 高级装饰器参数装饰器报错:Experimental support for decorators is a feature that is subject to change in a future release. Set the 'experimentalDecorators' option in your 'tsconfig' or 'jsconfig' to remove this warning. vscode 死活不识别这玩意,我服了 function logParameter(target: object, propertyKey: string, index: number) {
console.log(target, propertyKey, index)
}
class Person {
greet(@logParameter message: string, @logParameter name: string): string {
return `${message} ${name}`
}
}
const p = new Person();
p.greet('hello', 'xiaomuzhu') 装饰器工厂@logClass
class Person {
@logProperty
public name: string;
constructor(name : string) {
this.name = name;
}
@logMethod
public greet(@logParameter message : string) : string {
return `${this.name} say: ${message}`;
}
}
// 打印构造函数
function logClass(target: typeof Person) {
console.log(target)
}
// 打印属性名
function logProperty(target: any, propertyKey: string) {
console.log(propertyKey);
}
// 打印方法名
function logMethod(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
console.log(propertyKey);
}
// 打印参数位置
function logParameter(target: Object, propertyKey: string, index: number) {
console.log(index);
}
// name
// 0
// greet
// [Function: Person] function log(...args: any[]) {
switch(args.length) {
case 1:
return logClass.apply(this, args)
case 2:
return logProperty.apply(this, args)
case 3:
if (typeof args[2] === 'number') {
return logParameter.apply(this.args)
}
return logMethod.apply(this. args)
default:
thorw new Error('Decorators are not valid here!')
}
} |
typescript 简介
// function sayHello(person: string): string (+1 overload)
// Duplicate function implementation.ts(2393)
function sayHello(person: string){
return 'Hello, ' + person;
}
// let user: string
// Cannot redeclare block-scoped variable 'user'.ts(2451)
let user = 'Tom';
console.log(sayHello(user));
// 上面两处告警,是说声明的变量可能在全局被覆盖,ts不建议这么写,可以在代码 顶部 添加 export {} 解决 function sayHello(person) {
return 'Hello, ' + person;
}
var user = 'Tom';
console.log(sayHello(user)); 基础原始数据类型原始数据类型包括:布尔值、数值、字符串、null、undefined、Symbol、BigInt let isDone: boolean = false;
let createdByNewBoolean: Boolean = new Boolean(1);
let createdByNewBoolean: boolean = Boolean(1); let decLiteral: number = 6;
let hexLiteral: number = 0xf00d;
// ES6 中的二进制表示法
let binaryLiteral: number = 0b1010;
// ES6 中的八进制表示法
let octalLiteral: number = 0o744;
let notANumber: number = NaN;
let infinityNumber: number = Infinity; let myName: string = 'Tom';
let myAge: number = 25;
// 模板字符串
let sentence: string = `Hello, my name is ${myName}.
I'll be ${myAge + 1} years old next month.`; // void 表示没有任何返回值的函数
function alertName(): void {
alert('my name is tom');
}
// 声明一个 void 类型的变量没有什么用,因为你只能将它赋值为 undefined 和 null
let unnasble: void = undefined; let u: undefined = undefined;
let n: null = null;
// 由于 undefined 和 null 是所有类型的子类型,所以undefined类型可以复制给number类型
let num: number = undefined;
// 而void 类型的变量不能赋值给 number 类型的变量
let v: viod;
let num: number = u; // Type 'void' is not assignable to type 'number'. 任意值如果是一个普通类型,在赋值过程中改变类型是不被允许的 let myFavoriteNumber: string = 'seven';
myFavoriteNumber = 7; // error TS2322: Type 'number' is not assignable to type 'string'. let myFavoriteNumber: any = 'seven';
myFavoriteNumber = 7; // 在任意值上访问任何属性都是被允许的
let anyThing: any = 'hello';
console.log(anyThing.myName);
console.log(anyThing.myName.firstName); // 也允许调用任何方法
let anyThing: any = 'Tom';
anyThing.setName('Jerry');
anyThing.setName('Jerry').sayHello();
anyThing.myName.setFirstName('Cat');
// 变量如果在声明的时候,未指定其类型,那么他会被识别为任意类型
let something; // 等价于 let something: any;
something = 'seven';
something = 7;
something.setName('Tom') 类型推论
let myFavoriteNumber = 'seven';
myFavoriteNumber = 7;
// error TS2322: Type 'number' is not assignable to type 'string'.
// 以上代码等价于
// let myFavoriteNumber: string = 'seven';
// myFavoriteNumber = 7; 联合类型联合类型:标识取值可以为多种类型中的一种 let myFavoriteNumber: string | number;
myFavoriteNumber = 'seven';
myFavoriteNumber = 7; let myFavoriteNumber: string | number;
myFavoriteNumber = true; // error TS2322: Type 'boolean' is not assignable to type 'string | number'. 当typescript不确定一个联合类型的变量到底是那个类型的时候,我们只能访问此联合类型的所有类型里 共有的属性或方法 // 由于 length 不是 string 和 number 的共有属性,所以会报错
function getLength(something: string | number): number {
return something.length; // error TS2339: Property 'length' does not exist on type 'string | number'.
} 联合类型的变量在被赋值的时候,会根据类型推论的规则推断出一个类型 let myFavoriteNumber: string | number;
myFavoriteNumber = 'seven';
console.log(myFavoriteNumber.length); // 5
myFavoriteNumber = 7;
console.log(myFavoriteNumber.length); // error TS2339: Property 'length' does not exist on type 'number'. 对象的类型-接口interface Person {
name: string;
age: number;
}
let tom: Person = {
name: 'Tom',
age: 25
} interface Person {
name: string;
age: number;
}
let tom: Person = {
name: 'Tom'
};
// error TS2322: Type '{ name: string; }' is not assignable to type 'Person'.
// Property 'age' is missing in type '{ name: string; }'. interface Person {
name: string;
age: number;
}
let tom: Person = {
name: 'Tom',
age: 25,
gender: 'male'
};
// error TS2322: Type '{ name: string; age: number; gender: string; }' is not assignable to type 'Person'.
// Object literal may only specify known properties, and 'gender' does not exist in type 'Person'. 在赋值的时候,变量的形态必须和接口的形态保持一致 interface Person {
name: string;
age?: number;
}
let tom: Person = {
name: 'Tom';
age: 25;
}; interface Person {
name: string;
age?: number;
}
let tom: Person = {
name: 'Tom',
age: 25,
gender: 'male'
};
// error TS2322: Type '{ name: string; age: number; gender: string; }' is not assignable to type 'Person'.
// Object literal may only specify known properties, and 'gender' does not exist in type 'Person'. 需要注意的是:一旦定义了任意属性,name确定属性和可选属性的类型必须都是它的类型的子集 interface Person {
name: string;
age?: number;
[propName: string]: string;
}
let tom: Person = {
name: 'Tom',
age: 25,
gender: 'male'
};
// error TS2411: Property 'age' of type 'number' is not assignable to string index type 'string'.
// error TS2322: Type '{ [x: string]: string | number; name: string; age: number; gender: string; }' is not assignable to type 'Person'.
// Index signatures are incompatible.
// Type 'string | number' is not assignable to type 'string'.
// Type 'number' is not assignable to type 'string'. interface Person {
name: string;
age?: number;
[propName: string]: string | number;
}
let tom: Person = {
name: 'Tom',
age: 25,
gender: 'male'
} interface Person {
readonly id: number;
name: string;
age?: number;
[propName: string]: any;
}
let tom: Person = {
id: 89757,
name: 'Tom',
gender: 'male'
};
tom.id = 9527;
// error TS2540: Cannot assign to 'id' because it is a constant or a read-only property. 注意,只读的约束存在于第一次给对象赋值的时候,而不是第一次给只读属性赋值的时候(即 tom.id = xxxx 会触发只读约束) interface Person {
readonly id: number;
name: string;
age?: number;
[propName: string]: any;
}
let tom: Person = {
id: 89757,
name: 'Tom',
gender: 'male'
};
tom.id = 9527; 数组的类型let fibonacci: number[] = [1,1,2,3,5]; let fibonacci: number[] = [1, '1', 2];
// // Type 'string' is not assignable to type 'number'. let fibonacci: number[] = [1,1,2,3,5];
fibonacci.push('8');
// Argument of type '"8"' is not assignable to parameter of type 'number'. let fibonacci: Array<number> = [1,1,2,3,5]; interface NumberArray {
[index: number]: number
}
let fibonacci: NumberArray = [1,1,2,3,5] function sum(){
let args: number[] = arguments;
}
// Type 'IArguments' is missing the following properties from type 'number[]': pop, push, concat, join, and 24 more.
// 类数组,实际不是数组不能用普通数组的方式来描述,而应该用接口 // 常用的类数组都有自己的接口定义,如:IArguments NodeList HTMLCollection等
function sum(){
let args: {
[index: number]: number;
length: number;
callee: Function;
} = arguments;
} // any 允许数组中出现任意类型
let list: any[] = ['a', 2, {c: 'x'}] 函数的类型在JavaScript中,有两种常见的定义函数的方式:函数声明、函数表达式 function sum(x: number, y: number): number {
return x + y;
} function sum(x: number, y: number): number {
return x + y;
}
sum(1, 2, 3);
sum(1);
// error TS2346: Supplied parameters do not match any signature of call target.
// 输入多余或者少于要求的参数,是不被允许的 // typescript 中 => 用来表示函数的定义,左边是输入类型,需要用括号括起来,右边是输出类型
let mySum: (x: number, y: number) => number = function(x: number, y: number): number {
return x + y;
} // 采用函数表达式接口定义函数的方式时,对等号左侧进行类型显示,可以保证以后对函数名复制时,保证参数个数、参数类型、返回值类型不变
interface SearchFunc {
(source: string, subString: string): boolean
}
let mySearch: SearchFunc;
mySearch = function(source: string, subString: string): boolean{
return source.search(subString) !== -1;
} // 可选参数
// 需要注意的是,可选参数一定要在必选参数后面
function buildName(firstName: string, lastName?: string) {
if (lastName) {
return firstName + ' ' + lastName;
} else {
return firstName;
}
}
let tomcat = buildName('Tom', 'Cat');
let tom = build('Tom'); function buildName(firstName?: string, lastName: string) {
if (firstName) {
return firstName + ' ' + lastName;
} else {
return lastName;
}
}
let tomcat = buildName('Tom', 'Cat');
let tom = buildName(undefined, 'Tom');
// error TS1016: A required parameter cannot follow an optional parameter. // 参数默认值
// 在ES6中,我们允许给函数的参数添加默认值, typescript会将添加了默认值的参数识别为可选参数
function buildName(firstName: string, lastName: string = 'Cat') {
return firstName + ' ' + lastName;
}
let tomcat = buildName('Tom', 'Cat');
let tom = buildName('Tom'); // 此时,就不受 可选参数必须接到必需参数后面 的限制了
function buildName(firstName: string = 'Tom', lastName: string) {
return firstName + ' ' + lastName;
}
let tomcat = buildName('Tom', 'Cat');
let cat = buildName(undefined, 'Cat'); // 需要注意的是,剩余参数只能是最后一个参数
function push(array, ...items) {
items.forEach(function(item){
array.push(item)
})
}
let a: any[] = [];
push(a, 1, 2, 3); function push(array: any[], ...items: any[]) {
items.forEach(function(item){
array.push(item);
})
}
let a = [];
push(a, 1, 2, 3); // 重载
// 重载允许一个函数接受不同数量或类型的参数,做出不同的处理
function reverse(x: number | string): number | string | void {
if (typeof x === 'string') {
return Number(x.toString().split('').reverse().join(''))
} else if (typeof x === 'string') {
return x.split('').reverse('').join('')
}
}
// 这样有一个缺点,就是不能够精准的表达,输入数字的时候输出也应该是数字,输入字符串是,驶出也应该是字符串 // 使用多重 重载定义
function reverse(x: number): number;
function reverse(x: string): string;
function reverse(x: number | string): number | string | void {
if (typeof x === 'string') {
return Number(x.toString().split('').reverse().join(''))
} else if (typeof x === 'string') {
return x.split('').reverse('').join('')
}
} typescript 会优先从最前面的函数定义开始匹配,所以多个函数定义如果有包含关系,需要优先把精确的定义写在前面 类型断言// 语法
值 as 类型
<类型>值 interface Cat {
name: string;
run(): void;
}
interface Fish {
name: string;
swim(): void;
}
function isFish(animal: Cat | Fish) {
if (typeof animal.swim === 'function') {
return true;
}
return false;
}
// error TS2339: Property 'swim' does not exist on type 'Cat | Fish'. Property 'swim' does not exist on type 'Cat'. // 类型断言只能够欺骗,typescript编译器,无法避免运行时的错误
interface Cat {
name: string;
run(): void;
}
interface Fish {
name: string;
swim(): void;
}
function isFish(animal: Cat | Fish) {
if (typeof (animal as Fish).swim === 'function') {
return true;
}
return false;
} class ApiError extends Error {
code: number = 0;
}
class isApiError(error: Error){
if (error instanceof ApiError) {
return true;
}
return false;
} 将任何一个类型断言为 any (window as any).foo = 1; // 遇到 any 类型的变量时,我们可以选择通过类型断言 及时把 any 断言为 精确的类型
functon getCacheData(key: string): any{
return (window as any).cache[key];
}
interface Cat {
name: string;
run(): void;
}
const tom = getCacheData('tom') as Cat;
tom.run(); 类型断言的限制
interface Animal {
name: string;
}
interface Cat {
name: string;
run(): void;
}
let tom: Cat = {
name: 'Tom',
run: () => { console.log('run') }
}
let animal: Animal = tom; 在上面的例子中,Cat包含了 Animal 中的所有属性,除此之外,它还有一个额外的方法 run。typescript 并不关心 Cat 和 Animal 之间定义时是什么关系,而只会看他们最终的结构有什么关系 所以它 与 Cat extends Animal 是等价的 interface Animal {
name: string;
}
interface Cat extends Animal {
run(): void;
} interface Animal {
name: string;
}
interface Cat {
name: string;
run(): void;
}
function testAnimal(animal: Animal) {
return (animal as Cat);
}
function testCat(cat: Cat){
return (cat as Animal);
} 总上所述:
interface Cat {
run(): void;
}
interface Fish {
swim(): void;
}
function testCat(cat: Cat) {
return (cat as any as Fish);
}
// 若你使用了这种双重断言,很可能导致运行时错误 // 类型断言只会影响 typescript 编译时的类型,类型断言语句在编译结果中会被删除
function toBoolean(something: any): boolean {
return something as boolean
}
toBoolean(1); function toBoolean(something) {
return something;
}
toBoolean(1);
// 在上面例子中将 something 断言为 boolean 虽然可以通过编译,但是并没有什么用,代码最后编译如上
// 所以类型断言不是类型转换,它不会影响到变量的类型 function getCacheData<T>(key: string): T {
return (window as any).cache[key];
}
interface Cat {
name: string;
run(): void;
}
const tom = getCacheData<Cat>('tom')
tom.run(); 声明文件声明语句,指定引用第三方库的引用变量类型 declare var jQuery: (selector: string) => any;
jQuery('#foo') 通常我们会把声明语句放在一个单独的文件(file.d.ts)中,且声明文件必须以 .d.ts 为后缀 第三方声明文件,推荐使用 @types 统一管理第三方库的声明文件 npm install @types/jquery --save-dev 这块有点复杂 内置对象// 内置对象
let b: Boolean = new Boolean(1);
let e: Error = new Error('Error occurred');
let d: Date = new Date();
let r: RegExp = /[a-z]/;
// dom 和 bom 的内置对象
// Document HTMLElement Event NodeList
let body: HTMLElement = document.body;
let allDiv: NodeList = document.querySelectorAll('div');
document.addEventListener('click', function(e: MouseEvent) {
// do something
}) Math.pow(10, '2');
// error TS2345: Argument of type 'string' is not assignable to parameter of type 'number'.
document.addEventListener('click', function(e) {
console.log(e.targetCurrent);
});
// error TS2339: Property 'targetCurrent' does not exist on type 'MouseEvent'.
```@types/node --save-dev 进阶类型别名// 类型别名
// 类型别名常用与联合类型
type Name = string;
type NameResolver = () => string;
type NameOrResolver = Name | NameResolver;
function getName(n: NameOrResolver): Name {
if (typeof n === 'string') {
return n;
} else {
return n();
}
} 字符串字面量类型// 字符串字面量类型
type EventNames = 'click' | 'scroll' | 'mousemove';
function handleEvent(ele: Element, event: EventNames) {
// do something
}
handleEvent(document.getElementById('hello'), 'scroll');
handleEvent(document.getElementById('world'), 'dbclick'); // error TS2345: Argument of type '"dblclick"' is not assignable to parameter of type 'EventNames'. 需要注意的是:类型别名和字符串字面量类型都是使用 type 进行定义的 元组// 定义一堆值分别为 string 和 number 的元组
let tom: [string, number] = ['Tom', 25]; let tom: [string, number];
tom = ['Tom', 25];
tom.push('male');
tom.push(true);
// error TS2345: Argument of type 'boolean' is not assignable to parameter of type 'string | number'.
// 当添加一个越界元素时,它的类型会被限制为元组中没个类型的联合类型 枚举// 枚举(Enum)类型用于取值被先定在一定范围内的场景,比如一周只能有7天,颜色限定为红绿蓝等。
enum Days {Sun, Mon, Tue, Wed, Thu, Fri, Sat};
console.log(Days["Sun"] === 0); // true
console.log(Days["Mon"] === 1); // true
console.log(Days["Tue"] === 2); // true
console.log(Days["Sat"] === 6); // true
console.log(Days[0] === "Sun"); // true
console.log(Days[1] === "Mon"); // true
console.log(Days[2] === "Tue"); // true
console.log(Days[6] === "Sat"); // true // 手动复制,未被复制的枚举项会接着上一个枚举项递增
enum Days {Sun = 7, Mon = 1, Tue, Wed, Thu, Fri, Sat};
console.log(Days["Sun"] === 7); // true
console.log(Days["Mon"] === 1); // true
console.log(Days["Tue"] === 2); // true
console.log(Days["Sat"] === 6); // true enum Days {Sun = 3, Mon = 1, Tue, Wed, Thu, Fri, Sat};
console.log(Days["Sun"] === 3); // true
console.log(Days["Wed"] === 3); // true
console.log(Days[3] === "Sun"); // false
console.log(Days[3] === "Wed"); // true 类类与接口泛型声明合并function reverse(x: number): number;
function reverse(x: string): string;
function reverse(x: number | string): number | string {
if (typeof x === 'number') {
return Number(x.toString().split('').reverse().join(''));
} else if (typeof x === 'string') {
return x.split('').reverse().join('');
}
} interface Alarm {
price: number;
}
interface Alarm {
weight: number;
}
// 相当于
interface Alarm {
price: number;
weight: number;
} interface Alarm {
price: number;
}
interface Alarm {
price: string;
weight: number;
}
// error TS2403: Subsequent variable declarations must have the same type. Variable 'price' must be of type 'number', but here has type 'string'. interface Alarm {
price: number;
alert(s: string): string
}
interface Alarm {
weight: number;
alert(s: string, n: number): string;
}
// 相当于
interface Alarm {
price: number;
weight: number;
alert(s: string): string;
alert(s: string, n: number): string;
} 类的合并与接口的合并规则一致 代码检查目前以及将来的TypeScript的代码检查方案就是 typescript-eslint |
编译器的组成
编译器的处理
The text was updated successfully, but these errors were encountered: