Ts学习笔记

491 阅读6分钟

推荐一本开源书籍,TypeScript 入门教程

以下内容为总结这本书籍内容

参数新特性

参数类型

示例代码

// 参数类型
// 6中基本类型 string nubmer any(任意) boolen underfined symol(ES6)
var str: string = "asd";
// 如果给它赋值数字的话将会报错 但是编译成JS不会报错
// str = 123
var a = 1
// 自动类型识别 即使不指定 他会自己识别  也可以调用方法
var str1 = "a"    // 也不可以赋值数字
// str1 = 123;
// 如果想让他既可以是数字也可以是字符   
var str2: number | string; //这个叫联合类型
// 当调用他的方法的时候,必须是两者共有的方法
str2 = 2;
str2 = "123";

函数和参数指定类型

// 还可以指定为 基本类型中的    
// 如果传递的参数不是指定类型会报错 但仍会编译成js
// 可以定义默认值,如果不定义默认值必须穿两个参数
// 定义了之后可以只传一个
// b 是可选参数,记得要处理b没传的时候的情况
function text1(a: number, b?: number, c: number = 1): void {
    a++
    // console.log(b.length) 如果b没有传 则会报错
    // 如果return 会报错
}
//如果传递的参数不是指定类型会报错
// text1('123')
let mySum = function (x: number, y: number): number {
    return x + y;
};
//  这个实际上是这样  这里面的第一个=> 不是ES6 中的=> 这个是左边是输入,需要用括号括起来 右边是输出
let mySum: (x: number, y: number) => number = function (x: number, y: number): number {
    return x + y;
};

重载(函数的合并)

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('');
    }
    return 0
}

用接口定义函数的形状

暂时没看懂

ts.xcatliu.com/basics/type…

自定义类型(接口)

// 自定义类型  接口
interface Person {
    readonly id: number; // 只读属性 只允许在第一次给对象赋值的时候赋值,其他不可以。不允许被修改
    name: string
    age: number
    sex?: string  // 可选属性
    [propName: string]: any;  // 任意属性
    // 一旦定义了任意属性,那么确定属性和可选属性的类型都必须是它的类型的子集
}
// 定义的变量不允许比接口少或者多了一些属性  (如果定义了可选或者任意属性除外)
// 赋值的时候,变量的形状必须和接口的形状保持一致。
let tom: Person = {
    id: 213,
    name: 'Tom',
    age: 25
};

接口的合并

合并的属性的类型必须是唯一的

interface Alarm {
    price: number;
}
interface Alarm {
    weight: number;
    // price: string;
}
// 相当于
interface Alarm {
    price: number;
    weight: number;
}

如果 两个price的类型一样,虽然重复 但不会报错

如果类型不一致,则会报错

接口中方法的合并

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

数组的类型

注意:

  1. 数组的项中不允许出现其他的类型
  2. 用方法插入也不行

「类型 + 方括号」表示法

let fibonacci: number[] = [1, 1, 2, 3, 5];

用接口表示数组(一般不用)

// 表示只要索引是数字 值必须是数组
interface NumberArray {
    [index: number]: number;
}

let fibonacci: NumberArray = [1, 1, 2, 3, 5];

类数组

也就是伪数组

any 在数组中的应用

最常见

let list: any[] = ['xcatliu', 25, { website: 'http://xcatliu.com' }];

类型断言

语法

<类型>值
或者
值 as 类型
// 在 tsx 语法(React 的 jsx 语法的 ts 版)中必须用后一种。

例子

// 这样写会报错
function getLength(something: string | number): number {
    return something.length;
}
// 这样写也会报错  因为只能访问 公有的方法
function getLength(something: string | number): number {
    if (something.length) {
        return something.length;
    } else {
        return something.toString().length;
    }
}
// 使用类型断言
function getLength(something: string | number): number {
    if ((<string>something).length) {
        return (<string>something).length;
    } else {
        return something.toString().length;
    }
}

注意

类型断言不是类型转换,断言成一个联合类型中不存在的类型是不允许的

声明文件和内置对象

需要自行查看

ts.xcatliu.com/basics/decl…

TS进阶

类型别名

类型别名常用于联合类型。

type Name = string;
type NameResolver = () => string;
type NameOrResolver = Name | NameResolver;
function getName(n: NameOrResolver): Name {
    if (typeof n === 'string') {
        return n;
    } else {
        return n();
    }
}

字符串的字面量类型

就是 参数必须是EventNames的子集

type EventNames = 'click' | 'scroll' | 'mousemove';
function handleEvent(ele: Element, event: EventNames) {
    // do something
}

handleEvent(document.getElementById('hello'), 'scroll');  // 没问题
handleEvent(document.getElementById('world'), 'dbclick'); // 报错,event 不能为 'dbclick'

类型别名与字符串字面量类型都是使用 type 进行定义。

元组

例子 跟python中的元组差不多 只是定义不一样

// 定义一对值分别为 string 和 number 的元组:
let tom: [string, number] = ['Tom', 25];
// 可以进行单独的访问和赋值等
tom[0] = 'Tom';
tom[1] = 25;
// 但是当直接对元组类型的变量进行初始化或者赋值的时候,需要提供所有元组类型中指定的项。
tom = ['Tom', 25];
// tom = ['Tom']; 报错

属性和方法

class Person1 {
    // 公有私有属性 也可以定义方法 
    // protected 类的内部和子类可以访问
    // readonly  只读属性
    public name2: string
    private name1: string
    // 构造方法  this 是实例对象 不能被外部访问
    constructor(name: string) {
        this.name1 = name;
        this.name2 = name;
    }
    eat() {
        console.log(`i am eating ${this.name1}`)
    }
}
// 实例化对象
let person1 = new Person1('123')

继承

// 类的继承  extends 关键字
class Cat extends Person1 {
    constructor(name: string) {
        super(name); // 调用父类的 constructor(name)
        console.log(this.name2);
    }
    sayHi() {
        return 'Meow, ' + super.eat(); // 调用父类的 eat()
    }
}

储存器

// 储存器
class Animal {
    constructor(name: string) {
        this.name = name;
    }
    get name() {
        return 'Jack';
    }
    set name(value) {
        console.log('setter: ' + value);
    }
}

let cat = new Animal('Kitty'); // setter: Kitty
cat.name = 'Tom'; // setter: Tom
console.log(cat.name); // Jack

静态方法

使用 static 修饰符修饰的方法称为静态方法,它们不需要实例化,而是直接通过来调用

class Animal1 {
    static num = 42
    static isAnimal(a: any) {
        return a instanceof Animal;
    }
}

let jack = new Animal('Jack');
Animal1.isAnimal(a); // true
console.log(Animal3.num); // 42
// 报错 不需要实例化  通过类调用
// jack.isAnimal(a); // TypeError: a.isAnimal is not a function

ES7中类用法

ES6 中实例的属性只能通过构造函数中的 this.xxx 来定义

// ES7 中关于类的用法  实例属性
// ES6 中实例的属性只能通过构造函数中的 this.xxx 来定义
class Animal3 {
    name = 'Jack';
    constructor() {
        // ...
    }
}

let jack2 = new Animal3();
console.log(jack2.name); // Jack

TypeScript 中类的用法

public private 和 protected

三种 访问修饰符

  1. public修饰公有的,任何地方都可以访问。所有属性和方法默认public
  2. private 私有的,不能在声明它的类的外部访问
  3. protected 修饰受保护的,它和 private 类似,区别是它在子类中也是允许被访问的

readonly

只读属性关键字,只允许出现在属性声明或索引签名中。

示例

class Animal {
    readonly name;
    public constructor(name) {
        this.name = name;
    }
}

let a = new Animal('Jack');
console.log(a.name); // Jack
a.name = 'Tom';  // 报错

注意readonly 和其他访问修饰符同时存在的话,需要写在其后面。

示例

class Animal {
    // public readonly name;
    public constructor(public readonly name) {
        this.name = name;
    }
}

抽象类

abstract 用于定义抽象类和其中的抽象方法

抽象类是不允许被实例化的

抽象类中的抽象方法必须被子类实现

abstract class Animal {
    public name;
    public constructor(name) {
        this.name = name;
    }
    public abstract sayHi();
}

class Cat extends Animal {
    public sayHi() {
        console.log(`Meow, My name is ${this.name}`);
    }
}

let cat = new Cat('Tom');

如果实例化 Animal对象和没有实现 sayHi 方法都会报错

类的类型

与接口类似

class Animal {
    name: string;
    constructor(name: string) {
        this.name = name;
    }
    sayHi(): string {
      return `My name is ${this.name}`;
    }
}
let a: Animal = new Animal('Jack');
console.log(a.sayHi()); // My name is Jack

泛型

定义:泛型(Generics)是指在定义函数、接口或类的时候,不预先指定具体的类型,而在使用的时候再指定类型的一种特性。

  1. 泛型可以有多个类型参数

示例代码

// 代码缺陷   没有准确的定义返回值的类型
// Array<any> 允许数组的每一项都为任意类型  Array代表返回一个数组  类型any
function createArray(length: number, value: any): Array<any> {
    let result = [];
    for (let i = 0; i < length; i++) {
        result[i] = value;
    }
    return result;
}

console.log(createArray(3, 'x')) // ['x', 'x', 'x']

// 经过用泛型改良
function createArray1<T>(length: number, value: T): Array<T> {
    let result: T[] = [];
    for (let i = 0; i < length; i++) {
        result[i] = value;
    }
    return result;
}
// 调用的时候指定他的类型  也可以不指定让它自己识别
createArray1<string>(3, 'x'); // ['x', 'x', 'x']

//  泛型可以有多个类型参数
function swap<T, U>(tuple: [T, U]): [U, T] {
    return [tuple[1], tuple[0]];
}

console.log(swap([7, 'seven'])); // ['seven', 7]

// 泛型约束
// 普通
function loggingIdentity<T>(arg: T): T {
    // console.log(arg.length);  // 由于事先不知道它是哪种类型,所以不能随意的操作它的属性或方法
    return arg;
}

泛型约束

// 泛型约束
interface Lengthwise {
    length: number;
}
function loggingIdentity1<T extends Lengthwise>(arg: T): T {
    console.log(arg.length);
    return arg;
}
loggingIdentity1('asd')
// 调用 loggingIdentity 的时候,传入的值不包含length属性,那么报错
// loggingIdentity1(7)

// 多类型参数互相约束  没看懂
// T 继承 U,这样就保证了 U 上不会出现 T 中不存在的字段。
function copyFields<T extends U, U>(target: T, source: U): T {
    for (let id in source) {
        target[id] = (<T>source)[id];
    }
    return target;
}
let x = { a: 1, b: 2, c: 3, d: 4 };
console.log(copyFields(x, { b: 10, d: 20 }))

泛型接口 泛型类 等

具体请看

类实现接口

不同类之间有一些公有的特性

例子

// 定义接口
interface Alarm {
    alert(): void;
}
interface Light {
    lightOn(): void;
    lightOff(): void;
}
// 接口的继承
interface LightableAlarm extends Alarm {
    lightOn(): void;
    lightOff(): void;
}
// 定义类
class Door {
}
// 定义一个类继承Door 同时实现接口 Alarm
class SecurityDoor extends Door implements Alarm {
    alert() {
        console.log('SecurityDoor alert');
    }
}
// 类可以实现多个接口  也可以把 Alarm, Light替换为 LightableAlarm
class Car implements Alarm, Light {
    alert() {
        console.log('Car alert');
    }
    // 如果少些接口类中的任意一个都会报错
    lightOn() {
        console.log('Car light on');
    }
    lightOff() {
        console.log('Car light off');
    }
}

混合类型

暂时没看懂

以下内容

个人感觉

再讲

ES6

字符串新特性

多行字符串

双``,作用 :拼接字符串

示例代码

// 多行字符串 用``来表示 可以换行
var str: string = `a
asd
asd
asd
aaa`;

编译完成后的代码

"use strict";
var str = "a\nasd\nasd\nasd\naaa";

字符串模板

作用:在字符串中插入变量或者函数

示例代码:

// 字符串模板
var Myname: string = 'wuyichen'
var age: number = 12
var getName = function () {
    return 'wuyichen'
}
// 注意 只能在``里面用,如果是单引号或双引号的则按字符串执行
console.log(`hello ${Myname}`)
console.log(`hello ${getName()}`)
console.log("hello ${Myname}")

编译完成后的代码

// 字符串模板
var Myname = 'wuyichen';
var age = 12;
var getName = function () {
    return 'wuyichen';
};
// 注意 只能在``里面用,如果是单引号或双引号的则按字符串执行
console.log("hello " + Myname);
console.log("hello " + getName());
console.log("hello ${Myname}");

自动拆分字符串

第一个参数是字符串模板的值

第二个是 第一个变量

第二个是第三个变量

function text(tem: any, name: string, age: number) {
    console.log(tem)
    console.log(name)
    console.log(age)
}
text`my name is ${Myname},my age is ${age}`

// 编译成js输出结果
/*输出结果
    [ 'my name is ', ',my age is ', '' ]
    wuyichen
    12
 */

函数新特性

spread和rest 运算符

声明任意数量的方法

// spread和rest 运算符
function f1(...args: any) {
    args.forEach(function (arg: any) {
        console.log(arg)
    });
    // 这样里面不可以限制值 不报错
    //     args.forEach(element => {
    //         console.log(element)
    //     });
}

f1(1, 2, 3, 4, 5, 6)
f1(1, 2, 3, 4, 5)
// 还可以这样 不能跟此文件夹下的ts变量重名
let ab: number[] = [9, 8, 7, 6, 5]  // 数组类型的定义
f1(...ab)

generator函数

控制函数的执行过程,可以手工暂停和回复

目前没办法学。打印不出来

destructuring 析构表达式

通过表达式拆分数组或者对象 成任意数量的变量

跟ES6中的解构一样

对象

function getIBM() {
    return {
        code: 'IBM',
        price: 123,
        price2: {
            price3: 456,
            price4: 789
        },
        a: 'aaa',
        b: 'bbb'
    }
}
// 起别名 里面有其他指也不影响这个的使用
let { code: condex, price, price2, price2: { price3 } } = getIBM()
// 如果返回的是一个对象的话,则price2是一个对象,里面的可以可以通过price2: { price3 }拿到里面的属性

console.log(condex)
console.log(price)

数组

参考ES6的解构

循环和表达式

表达式

箭头表达式 ES6一样 有一个参数不需要()

主要用来解决this 指针的问题

var sum = (a: number, b: number) => a + b

循环

forEach

// forEach 不能被break
let aa: any[] = [4, 5, 6, 7, 8, 9]
// ts中不能添加
// aa.desc = "456789"
aa.forEach(value => console.log(value))

for in

for (let a in aa) {
    console.log(a)
    // 如果想打印值 需要这样打印
    console.log(aa[a])
}