Ts学习笔记

  • any 任何类型都可以赋值给anyany也可以给任何类型赋值
  • unknown 任何类型可以赋值给 unknown ,但是 unknown 类型赋值给其它类型需要对其进行类型缩小。
type
  • 类型一般都是大写字母开头
type Fish = {
    name: string,
    swim: () => void
}
类型谓词
  • 返回一个布尔类型
  • is XXX
type Fish = {
    name: string,
    swim: () => void
}

type Bird = {
    name: string,
    fly: () => void
}

type Animal = Fish | Bird;

function run(ani: Animal): ani is Fish {
    return (ani as Fish).swim !== undefined;
}

const res1 = run({
    name:"jinyu",
    swim:()=>{

    }
})
const res2 = run({
    name:"maque",
    fly:()=>{
        
    }
})
console.log(res1,res2); // true false
调用签名
  • 可以给函数添加属性
  • 不可以给函数定义name属性名
type Func = {
    fname: string,
    (s: string): void
}

function runFun(fn: Func) {
    fn('hello world');
}


function testFn(s: string) {
    console.log(s);
}
testFn.fname = 'strReturnVoid';
runFun(testFn);
构造签名
  • 类型参数是用来关联多个值的类型的。如果一个类型参数在函数签名中只使用一次,那么他是没有必要再次定义的
class Animal {
    cname: string
    constructor(name: string) {
        this.cname = name;
    }
}

type classsFunc = {
    new(s: string): Animal
}

function runFun(fn: classsFunc, cname: string): Animal {
    return new fn(cname);
}

const cat = runFun(Animal, 'cat');
console.log(cat.cname);  // cat
可选参数
  • ?
  • 不能与参数默认值同时使用
    function fu1(n?:number){
        return n;
    }

    function fn2(n:numbner = 12.548){
        return n.toFixed(2);
    }
  • 当为回调写一个函数类型时,永远不要写一个可选参数,除非你打算在不传递该参数的情况下调用函数。
函数重载
  • 必须得有一个实现函数
  • 这个实现函数包括全部函数重载的参数,并且返回值与其他函数返回值相同
function makeDate(timestap: number): Date;
function makeDate(m: number, d: number, y: number): Date;
function makeDate(mOrTimestap: number, d?: number, y?: number): Date {
    if (d !== undefined && y !== undefined) {
        return new Date(mOrTimestap, y, d);
    } else {
        return new Date(mOrTimestap);
    }
}

const date1 = makeDate(1236546544);
const date2 = makeDate(2022, 4, 1);

console.log(date1,date2);
重载签名和实现签名出错原因
  • 参数不正确
  • 参数类型不正确
  • 返回类型不正确
  • 我们只能看到重载函数的参数约束,无法看到实现函数的参数约束

如何编写好的重载函数

  • 在可能的情况下, 总是倾向于使用联合类型的参数而不是重载参数
  • this 可以直接用作函数的参数,但是不建议这么做
函数的其他类型
  • void 函数无返回值
  • object 任何的不是基元的值 (string numbner bigint boolean symbol null undefined)
    • 不同于{} 不同于Object
    • object 不是 Object 。在 ts 中始终使用 object
  • unknown 类型代表任何值。这与any类型类似,但更安全,因为对未知的unknown值做任何操作都是不合法的
    • unknown 类型的值赋值给其他类型的值的时候,需要对其进行类型缩小才可以进行赋值,其他类型的值都可以赋值给 unknown
  • never 永远不会被观察到的值
    • 抛出异常
    • 函数终止
    • 死循环
  • Function
    • 全局性的Function类型描述了诸如bind、call、apply和其他存在于Javascript中所有函数值的属性。
    • Function 类型的值总是可以被调用,这些调用返回any
参数展开运算符
  • 形参展开 …
  • 实参展开 …
    const args = [8,5] as const;
    const angle = Math.atan2(...args);
参数解构
  • const {a,b} = {a:1,b:2}
返回void 类型
  • 一个返回void类型的函数,可以返回任意类型,但是返回的数据会被忽略
  • 当一个字面的形式定义的函数返回void类型的时候,该函数必须不返回任何东西
type Fun = () => void 
对象类型
  • type interface

  • 属性修改器

    • ?:可选属性
    • readonly age:number 只读属性
      • (对基本类型起作用,复杂数据类型只是对地址进行了固定)
      • 可以通过 再次定义readonly 的方式对其进行再次只读
      • 在对象给对象赋值的时候,ts不考虑该对象的属性是否是只读属性。readonly的属性可以通过别名改变
    • 索引签名
      • 索引签名和只读属性可以使数组中的数据不被改变
    • 扩展类型
      • extends
      • 可继承多个定义的接口。之间用逗号隔开
      • ***两个同名 interface 会自动进行合并,两个同名type会出现同名错误 ***
    • 默认值
  • 解构的时候最好不要在对象中使用类型,它与es6的别名方法冲突

交叉类型
  • type TypeArr = 类型1 & 类型2
泛型对象类型
 type Box<T> = {
    content: T
}

type OrNull<T> = T | null; // 可以是 T 类型或者为 null

type OneOrMany<T> = T | T[]; // 可以是 T 类型,或者是T类型的数组

type OneOrManyOrNull<T> = OneOrMany<OrNull<T>>   // 可以是 T 类型、null类型、T类型的数组。

泛型函数
  • 泛型函数的限制条件 extends { length:number }
  • 使用受限值
    • 指定类型参数 <number|string>
    • 防止 ts 自动类型推断
  • 编写优秀通用函数的准则
    • 可能的情况下,使用类型参数本身,而不是对其进行约束
    • 尽可能少的使用类型参数
    • 如果一个类型的参数只出现在一个地方,请重新考虑你是否真的需要他
    function f1<T>(arr:T[]){
        return arr[0];
    }

    function f2<T extends any[] >(arr:T[])
    {
        return arr[0]
    }

#### 从类型中创建泛型

  • 泛型类型
  • keyof类型操作符
    • keyof类型操作符后面必须指的是一个类型
  • typeof类型操作符
    • typeof后面必须是一个变量
  • 索引访问类型
  • 条件类型
  • 映射类型
  • 模板字面量类型
泛型
  • 声明泛型函数后,可以使用两种方式进行调用。一种是直接输入参数调用,让ts进行类型推断。一种是直接显式声明类型
  • 泛型函数的应用
function identity<T>(params: T): T {
    return params;
}

let myIndentity: <T>(arg: T) => T = identity;

let myIndentity2: <I>(arg: I) => I = identity;

let myIdenttity3: { <T>(arg: T): T } = identity;

interface myIndentity4 {
    <T>(arg: T): T
}
let myIdenttity4:myIndentity4 = identity;
  • 泛型接口的应用

  interface Animal<T> {
    name:T,
    skip:(arg:T)=>T
  }

  • 泛型类的应用
class Dog<T>{
    name: T
    constructor(name: T) {
        this.name = name;
    }
    getName(): T {
        return this.name;
    }
}
  • 泛型约束

function getLength<T extends { length:number } >(params: T) {
    return params.length;
}

function getLength<T>(params: Array<T>) {
    return params.length;
}

  • 在泛型约束中使用类型参数

// Key 类型属于 Type 对象类型中的某个 Key 值, 就是 Key 的取值范围是在 Type 对象的所有 key 值中获取的
function getProperty<Type, Key extends keyof Type>(obj: Type, key: Key) {
    return obj[key]
}

  • 在泛型中使用类类型
  // 创建类的实例,工厂函数
function create<Type>(c: { new(): Type }): Type {
    return new c();
}
keyof 类型操作符
  • 用来接收一个对象类型,他会产生key的字符串或者是数字字面量的一个结合或者是一个联合类型
  type Point = { x:number,y:number}
  type P = keyOf Point // P 类型属于Point对象类型中的某个 key 值
  const p1:P = 'x';
  const p2:P = 'y';
typeof 类型操作符
  • 只能去修饰某个变量或者是某个变量的属性,不要去标识函数的调用结果 tips:不是修饰的具体的变量的值
  • ReturnType T:必须是函数类型 ts中预定义的类型 tips:T必须是函数类型(type/interface声明的类型)
    • 该关键字返回的是 T 函数执行后的返回的类型
 let str = 'hello'; // string
 let str2 = typeof str; // string
 str2 = 'world';

type Fun = (arg: number) => boolean;

type K = ReturnType<Fun>;

function fn(): K { // boolean
    return true;
}

function fn2() {
    return {
        x: 10,
        y: 20
    }
}

// 该方式不可取
let fn3: typeof fn2;

fn3 = function () {
    return {
        x: 222,
        y: 666
    }
}

let fn4: ReturnType<typeof fn2>;

fn4 = {
    x: 111,
    y: 222
}
索引类型

    type Person = {
        name:string,
        age:number
    }

    type Age = Person['age']; // == number
    let age:Age = 666;
    type Person2 = [keyof Person];
    let per1:Person2; // number / string 

条件类型

interface IdLabel {
    id: number
}

interface NameLable {
    name: string
}

type NameOrId<T extends number | string> = T extends number ? IdLabel : NameLable;

function createLable<T extends number | string>(idOrName: T): NameOrId<T> {
    throw ''
}

createLable('hello');
createLable(666);

条件类型约束

type Flatten<T> = T extends any[] ? T[number] : T;
// string
type Str = Flatten<string[]>;
// number
type Num = Flatten<number>

type MessageOf<T> = T extends { message: unknown } ? T['message'] : never;

type Address = {
    message: string
}
// string
let str: MessageOf<Address>;

在条件类型内推理
type GetReturnType<Type> = Type extends (...args: never[]) => infer Return ? Return : never;
// number
type Num = GetReturnType<() => number>;

let num: Num = 111;
// string
type Str = GetReturnType<() => string>;

let str: Str = '111';
// boolean[]
type Bol = GetReturnType<(a: boolean, b: boolean) => boolean[]>;

let bol: Bol = [true];
// never
type Nev = GetReturnType<number>;

let nev: Nev = 11 as never;

function NumOrStr(arg: string | number): string | number {
    return typeof arg === 'string' ? arg : arg + 1;
}

// number 、string
type NumOrS = ReturnType<typeof NumOrStr>;

let s:NumOrS = 'ss';

分布式条件数组

type toArray<T> = T extends any ? T[] : never;

type StrOrNumArr = toArray<string | number>;

let arr1:StrOrNumArr = [1,2,3];
let arr2:StrOrNumArr = ['s','s','d'];

  • 知识点补充
    • 类的所有方法都是定义在class的prototype上面的
    • 类的内部有setter / getter 方法可以在设置或获取类的属性值的时候,对其进行拦截操作
    • 类也有隐藏的的name属性 指的是class关键字后面紧跟的类名
    • 除了私有属性,其他的属性和方法包括静态方法都会被子类继承
      • 静态方法不需要实例化对象来调用,可以直接通过类名.的形式来调用
      • 私有属性只能在类内部进行使用
    • super 在这是指父类的构造函数,用来新建一个父类实例对象
      • super 两种含义
        • 作为函数来使用,super 指的是父类的构造函数,返回的是子类的实例
          • 作为函数使用的时候,必须是在构造函数中进行调用
        • 作为对象来使用。super 指向的是父类的原型,所以只能调用父类原型上的属性和方法,父类实例上的属性不可调用。
          • 调用的方法中的 this 指向的子类实例。
          • 在静态方法中 super 指向的是父类,调用返回的是子类
          • 使用super.xxx 对属性进行赋值的时候,此时的super指的是子类的this
    • 在继承父类后,子类必须得在构造函数中执行 super(),原因是因为,es6的this实例是在继承父类的属性和方法之后生成的。在对其进行添加自己的属性和方法
    • 所以不可以在 super() 之前使用 this 关键字
      • es5的继承机制:先创造一个独立子类实例对象,再将父类的方法添加到这个对象上,即实例在前,继承在后
      • es6的继承机制:先将父类的属性和方法添加到一个空对象上,再将该对象作为子类的实例,即继承在前,实例在后
    • 如果子类没有显式书写constructor 构造函数,在子类继承父类后,会隐式生成constructor构造函数,并且在构造函数内部会自动执行super()
    • Object.getPrototypeOf() 获取类的父类
    • mixin 混入的实现将多个对象合成一个对象
      • 解构操作符 ...
      • Object.assgin()
      • Reflect.ownKeys() 获取对象的全部key值组成一个数组并返回
      • Object.getOwnPropertyDescriptor() 方法返回指定对象上一个自有属性对应的属性描述符。(自有属性指的是直接赋予该对象的属性,不需要从原型链上进行查找的属性)
      • Object.getOwnPropertyNames()方法返回一个由指定对象的所有自身属性的属性名(包括不可枚举属性但不包括Symbol值作为名称的属性)组成的数组。
      • hasOwnProperty() 实例检查自身是否有这个属性,不包括所属类的原型上的属性
  • 类属性
    • 属性!:type 可以排除该属性不为 null 或 undefined
  • readonly
    • readonly属性 只读属性
    • 可以在构造函数中对只读属性进行赋值 及 在类定义初始化默认值的时候
    • 其他情况下不可以对只读属性进行修改
  • 构造器
    • 构造函数不能有类型参数
    • 构造函数不能有返回类型注释
  • 方法
    +
  • Getters / Setters
    • 如果只存在get,但没有set,则该属性自动是只读的
    • 如果没有指定setter参数的类型,他将从getter的返回类型中推断出来
    • 访问器和设置器必须具有相同的成员可见性
    • 设置setter函数 传参数的时候,其参数的值的类型必须为getter函数返回值的类型
    • Number.isFinite 标识这个数是不是可数的
  • 索引签名
// s的类型定义 约束了Animal类里面的函数和属性的类型
class Animal {
    [s: string]: boolean | ((s: string) => boolean)
    x = true;

    check(s:string) {
        return this[s] as boolean;
    }
}

  • 类的继承
    • implements
    • 可以实现多个接口
    • 类实现接口的时候,实现的接口的方法和属性不一定要和接口中定义的属性和方法一致,只要兼容就可以
    • 实现带有可选属性的接口,并不能实现可选属性
interface Person {
    age: number
    run(): void
}

interface Job {
    address: string
    getAddress(): void
}

class Man implements Person, Job {
    address: string;
    age: number;
    run(): void {
        console.log("跑的相对快");

    }
    getAddress(): string {
        return this.address;
    }
}
  • extends
  • 类去继承其他的类
  • 子类(派生类)继承父类(基类)重写父类的方法的时候,需要注意方法参数的兼容性问题
  • 初始化顺序
    • 基类的属性初始化,基类的构造函数执行
    • 派生类的属性初始化,派生类的构造函数执行
继承内置类型
  • extends Error
  • 继承内置类型的时,在es5版本之后没问题,在es5版本之前继承内置类需要自己手动设置原型
class MsgError extends Error {
    message: string;
    constructor(s: string) {
        super();
        this.message = s;
        Object.setPrototypeOf(this, MsgError.prototype);
    }
    throwMsg() {
        return this.message;
    }
}
类成员的可见性控制
  • private / #
    • 私有的。只能在当前类中进行访问
  • public
    • 公开的,任何对象在任何地方都可以访问
  • protected
    • 受保护的。能在当前类或子类中进行访问
  • 静态成员
    • 通过类名进行访问
    • 可以在static前面加上可见性控制
    • 在静态函数中,使用this ,this指向的是当前类。而不是指向当前类的实例
  • static 区块
    +
  • 泛型类
    • 静态成员不能使用类类型
  • this 的类型缩小
  
class FileSystemObject {
    // this 的类型缩小
    isFile(): this is FileRep {
        return this instanceof FileRep;
    }

    isDirectory(): this is Directory {
        return this instanceof Directory;
    }

    isNetWorked(): this is NetWorked & this {
        return this.networked;
    }

    constructor(public path: string, private networked: boolean) {

    }
}
  • 参数属性
    class Animal {
    skip: string
    constructor(public s:string){
    }
}
let cat = new Animal('hello');
// 可直接访问
console.log(cat.s);

  • 类表达式
    const Animal = class <T>{
    age: T
    getAge():T {
        console.log(this.age);
        return this.age;
    }
}
  • 抽象类
    abstract class Animal {
    abstract age: number
    abstract getAge(): void
}

class Cat extends Animal {
    age: number;
    getAge(): void {
        console.log(this.age);
    }
}

let kf = new Cat();
kf.age = 3;
kf.getAge();
 
function fuc(Ctor: new () => Animal) {
    const cat = new Ctor();
    cat.age = 6;
    cat.getAge()
}
// 所要填写的参数类不能为抽象类,并且为Animal类的子类
fuc(Cat);
  • 类之间的关系

    • 类B的包含类A的全部的属性和方法,类B的类型可以接受类A的实例
    • 未给类声明任何属性和方法。拿类做参数类型,可以穿任意类型的值
  • 模块 es moudle

    • es2015中 import 和 export 的文件都被认为是一个模块,没有使用import 和export 的被认为是脚本文件,他是可以在全局范围内执行的
import XXX from '' / export default{}
import {} from '' / export 
import XXX ,{} from  '' export ,export default
  • ts特定的模块方法
    • 可以导出导入类型 type interface
import { Animal,type Cat,type Dog } from ""
  • ES模块语法和CommonJS行为
    import fs = require('fs');
    const code = fs.readFileSync('./b.ts','utf-8');
  • CommonJS 语法 require
    • 导出 exports moudle.exports
    • 导入 require
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值