ts学习总结

一、简介

TypeScript是微软开发的一个开源的编程语言,通过在JavaScript的基础上添加静态类型定义构建而成。TypeScript通过TypeScript编译器或Babel转译为JavaScript代码,可运行在任何浏览器,任何操作系统。

1. ts官网

https://www.tslang.cn/

2. ts安装

// 安装:
npm install -g typescript

// 查看版本
tsc -v

3. ts运行

tsc test.ts

// 监听当个文件 实时编译
tsc test.ts -w

// 监听全部ts文件
tsc -w

// 编译所有ts文件
tsc

注:编写ts文件,运行ts文件是先编译成js后在执行js,想直接执行ts,可安装ts-node。

4. ts-node

这个组件是为了让我们直接执行 ts,省去了我们手动转换 js,它内部可以直接转换 js,然后直接运行,使用命令安装:

npm install -g ts-node

运行

ts-node xxx.ts

二、基础类型

js基础类型分为两种:原始类型和对象类型,其中原始类型包括:数值、字符串、布尔值、null、undefined以及es6中的symbol、biignt。
ts支持与js几乎相同的数据类型。

1、布尔值/字符串/数值

//var [变量名] : [类型] = 值;
let isDone: boolean = false;
const num: number = 1;
const name: string = "猪猪";

//字符串模板依旧能用
const str:string=`${name}`

//声明变量的类型,但没有初始值,变量值会设置为 undefined:
//var [变量名] : [类型];
var test: string;

//如果变量的声明和赋值是同时进行的,ts可以自动对变量进行类型检测。
//var [变量名] = 值;
var uname = "abc";

//声明变量没有设置类型和初始值,类型可以是任意类型,默认初始值为 undefined:
//var [变量名];
var cord;

(1)num声明变量的类型number之后,后面赋值时,得跟数值,否则则会报错,因为变了num的类型是数值。

let num: number;
num = "123"; // 报错

(2)如果变量的声明和赋值是同时进行的,ts可以自动对变量进行类型检测。

let num = 123;
num = "123"; // 报错

2、null和undefined

//这两个类型只有 自己
const onlyNull: null = null;
const onlyUndefined: undefined = undefined;

3、数组

//数组泛型,Array<元素类型>
let list: Array<number> = [1, 2, 3];

//string[] 字符串数组
let list: string[];
list = ['a', 'b', 'c'];

//number[] 数值数组
let list: number[] = [1, 2, 3];

4、元组

元组 元组就是固定长度的数组

// 语法:[类型, 类型, 类型]
let list: [string, number];
list = ['hello', 123];

list = ['hello'];// 报错
list = [123, 'hello']; // 顺序不一致
list = ['hello', 123, 11]; // 多余

5、枚举

一个枚举可以用 enum 关键字来定义。

5.1. 数值型枚举

enum Direction {
  Up = 1, 
  Down, 
  Left, 
  Right,
}

上面,我们有一个数字枚举,其中 Up 被初始化为 1 ,所有下面的成员从这一点开始自动递增。换句话说,Direction.Up的值是 1 ,Down 是 2,Left是3,Right是4。
如果我们愿意,我们可以完全不使用初始化器:

enum Direction {
  Up,
  Down,
  Left,
  Right,
}

这里,Up的值是0,Down是1,依次类推。这种自动递增的行为对于我们可能不关心成员值本身,但关心每个值与同一枚举中的其他值不同的情况很有用。

使用枚举很简单:只需将任何成员作为枚举本身的一个属性来访问,并使用枚举的名称来声明类型:

enum UserResponse {
  No = 0,
  Yes = 1,
}
 
function respond(recipient: string, message: UserResponse): void {
  // ...
}
respond("Princess Caroline", UserResponse.Yes);

数字枚举可以混合在计算和常量成员中(见下文)。简而言之,没有初始化器的枚举要么需要放在第一位,要么必须放在用数字常量或其他常量枚举成员初始化的数字枚举之后。换句话说,下面的情况是不允许的:

enum E {
  A = getSomeValue(),  
  B,  
  // Ⓧ Enum成员必须有初始化器。
}

5.2. 字符串枚举

字符串枚举是一个类似的概念,但有一些细微的运行时差异,如下文所述。在一个字符串枚举中,每个成员都必须用一个字符串字头或另一个字符串枚举成员进行常量初始化。

enum Direction {
  Up = "UP", 
  Down = "DOWN", 
  Left = "LEFT", 
  Right = "RIGHT",
}

虽然字符串枚举没有自动递增的行为,但字符串枚举有一个好处,那就是它们可以很好地 “序列化”。换句话说,如果你在调试时不得不读取一个数字枚举的运行时值,这个值往往是不透明的–它本身并不传达任何有用的意义(尽管 反向映射往往可以帮助你),字符串枚举允许你在代码运行时给出一个有意义的、可读的值,与枚举成员本身的名称无关。

5.3. 异构枚举

技术上讲,枚举可以与字符串和数字成员混合,但不清楚为什么你会想这样做:

enum BooleanLikeHeterogeneousEnum {
  No = 0,  
  Yes = "YES",
}

除非你真的想以一种巧妙的方式利用JavaScript的运行时行为,否则建议你不要这样做。

6、| 和 &

// | 或 表示可以是字符串或数值
let str: string | number;

str = 1;
console.log(str.length)// 报错
str = 'hello';
console.log(str.length)// 正常运行
str = false; //报错

//& 表示同时
let str2: {name: string} & {age: number};
str2 = {name: '嘿嘿'}; //报错
str2 = {name: '嘿嘿', age: 18};

7、type

// type
//类型的别名
type myType = string;
let k: 1 | 2 | 3 | 4;
let m: myType //m 类型是string

type myType2 =  1 | 2 | 3 | 4;
k = 1;
k = 6;//报错

8、function

//js不考虑参数的类型和个数的
function sum(a, b) {
  return a + b;
};

// ts 隐式any
function sum(a: any, b:any):any {
    return a + b;
};

function sum2(a:number, b:number): number {
    return a + b;
};

let s1 = sum('1', 2); // 12
let s2 = sum(2, 2); // 4
let s3 = sum2(1, 5); // 6

console.log(s1, s2, s3);

9、任意值

任意值(any/unknown)用来表示允许赋值为任意类型

9.1. any

//any表示任意类型的值,一个类型使用了any后相当于关闭了ts的类型检测
//在使用ts时 不建议使用any

let a: any

a = 1;
a = 'hello';
a = false;

//如果变量不声明类型,则ts解析器会自动判断变量的类型为any(隐式any)
let b;

b = 1;
b = 'hello';
b = true;

let s: string;

//b的变量是any,它可以赋值给任意变量
s = b;

9.2. unknown

//unknown 表示未知类型的值

let c: unknown

c = 1;
c = 'hello';
c = false;

let s: string;

c = 'world';
// 把c赋值给s,也就是把未知变量赋值给其他类型变量会报错
s = c;//报错

//unknown 实际上就是一个类型安全的any
//unknown 不能直接赋值给任意变量

//判断类型
if (typeof c === 'string') {
    s = c;
}

//类型断言 用来告诉解析器变量的实际类型
s = c as string;
s = <string> c;

10、void

const u:void=undefined;
const n:void=null;
const age:void=18;//不能将number类型赋值给void

//void 用来表示空 以函数为例 就表示没有返回值的函数
function fn() {
    
}
function fn2(): void {

}

11、never

//never 表示永远不会返回结果
function fn(): never {
    throw new Error('错误的');
}

// 特殊情况下,变量也可以使用never声明,比如一个永远为空的数组。never是任何类型的子类型,也可以赋值给任何类型,但是反过来不可以。(自身除外)
const emptyArr: never[] = []

12、object

//object 表示一个js对象
let a: object;
a = {}
a = function() {}

//{}用来指定对象中可以包含哪些属性
//语法:{属性名:属性值}
//属性名后面加上? 表示属性是可选的
let b: {name: string, age?: number};

b = {};//报错 缺少属性名

b = {name: '阿三'};
b = {name: '李四', age: 12};

// [propName: string]: any 表示任意类型的属性
let c: {name: string, [propName: string]: any};
c = {name: '嘿嘿', age: 12, gender: '男'};

//设置函数结构的类型声明
//语法:(形参:类型, 形参:类型...) => 返回值
let d: (a: number, b: number) => number;
d = function(n1, n2):number {
    return n1 + n2;
}

//d = function(n1: string, n2:string): number {
//     return 10;
// }
//报错

13、symbol

symbol使用依赖es6编译辅助库 ,tsconfig.json lib[“es6”]

const sym1: symbol = Symbol();
const sym2: symbol = Symbol();
console.log(sym1===sym2); //false

let sym3 = Symbol("key");
let sym4 = Symbol("key");

sym2 === sym3; // false, symbol是唯一

三、接口

1. 定义

TypeScript 中的接口是一种抽象结构,用于定义对象的类型。接口定义了对象应该包含的属性和方法,但不提供实现。

TypeScript 的接口类似于其他编程语言中的接口或抽象类,但不同于它们,接口可以描述对象的形状,包括对象具有哪些属性和方法、属性和方法的类型和返回值类型等。

2. 属性接口

属性接口是一种描述对象属性的接口,用来规范对象的属性名、属性类型和属性值等信息。以下是一个属性接口的示例:

interface Person {
  name: string;
  age: number;
}

function greet(person: Person) {
  console.log(`Hello, my name is ${person.name} and I'm ${person.age} years old.`);
}

let john: Person = {
  name: "John",
  age: 18
};

greet(john); // 输出:Hello, my name is John and I'm 18 years old.

在这个示例中,我们定义了一个 Person 接口,它包含了一个 name 属性和一个 age 属性。然后,我们创建了一个john对象,它符合 Person 接口的定义,并将它传递给greet函数进行问候。由于 john 对象的属性和类型与 Person 接口的定义一致,所以可以顺利通过编译并输出问候语。

3. 可选属性

有时候我们需要定义一个对象的属性是可选的,即这个属性可以存在也可以不存在。在 TypeScript 中,我们可以使用 ? 符号来表示一个属性是可选的。以下是一个可选属性的示例:

interface Person {
  name: string;
  age?: number;
}

function greet(person: Person) {
  console.log(`Hello, my name is ${person.name}${person.age ? " and I'm " + person.age + " years old." : "."}`);
}

let john: Person = {
  name: "John"
};

let jane: Person = {
  name: "Jane",
  age: 20
};

greet(john); // 输出:Hello, my name is John.
greet(jane); // 输出:Hello, my name is Jane and I'm 20 years old.

在这个示例中,我们将 age 属性定义为可选的,即可以存在也可以不存在。然后,在 greet 函数中,我们使用了一个三目运算符来判断 age 属性是否存在,并根据不同的情况输出不同的问候语。

##4. 只读属性

只读属性,即属性只能在对象创建时被赋值,以后不能被修改。使用 readonly 关键字来定义只读属性。以下是一个定义只读属性的接口示例

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

let p: Point = { x: 10, y: 20 };
console.log(p.x); // 输出 10

p.x = 5; // 报错,因为 x 是只读属性

在这个示例中,我们定义了一个 Point 接口,它具有两个只读属性 x 和 y。然后,我们创建了一个符合 Point 接口的对象 p,并赋值了两个属性。最后,我们尝试修改 p.x 的值,但 TypeScript 报错,因为 x 是只读属性,不能被修改。

注意:只读属性不允许被赋值后再修改,但是如果一个只读属性是一个对象,那么该对象中的属性是可以被修改的,因为只读属性只是保证属性引用不变,而不是属性值不变。

5. 函数接口

函数接口是一种描述函数类型的接口,用来规范函数的参数类型和返回值类型等信息。以下是一个函数接口的示例:

interface Greeter {
  (name: string): string;
}

let greet: Greeter = function(name: string) {
  return `Hello, ${name}!`;
};

console.log(greet("John")); // 输出:Hello, John!

在这个示例中,我们定义了一个 Greeter 接口,它表示一个参数为 name,返回值为字符串的函数类型。然后,我们创建了一个名为 greet 的函数,并将它赋值给 Greeter 接口,这样 greet 就符合 Greeter 接口的定义了。最后,我们调用 greet 函数并输出它的返回值。

6. 可索引接口

可索引接口是一种描述数组和对象的接口,用来规范它们的索引类型和索引值类型等信息。以下是一个可索引接口的示例

interface StringArray {
  [index: number]: string;
}

let colors: StringArray = ["Red", "Green", "Blue"];

console.log(colors[0]); // 输出:Red

7. 类接口

类接口是一种描述类类型的接口,用来规范类的成员和属性等信息。以下是一个类接口的示例:

interface Person {
  name: string;
  age: number;
  greet(name: string): void;
}

class Student implements Person {
  constructor(public name: string, public age: number) {}

  greet(name: string) {
    console.log(`Hello, ${name}, I'm ${this.name}.`);
  }
}

let john: Person = new Student("John", 18);

john.greet("Jane"); // 输出:Hello, Jane, I'm John.

在这个示例中,我们定义了一个 Person 接口,它表示一个具有 name 属性、age 属性和 greet 方法的类类型。然后,我们创建了一个 Student 类,并将它实现 Person 接口,这样 Student 就符合 Person 接口的定义了。最后,我们创建了一个 john 对象,并调用它的 greet 方法输出问候语。由于 john 对象是 Student 类的实例,而 Student 类实现了 Person 接口,所以 john 对象也符合 Person 接口的定义。

8. 继承接口

继承接口是一种描述接口继承关系的接口,用来规范接口之间的关系。以下是一个继承接口的示例:

interface Shape {
  color: string;
}

interface Circle extends Shape {
  radius: number;
}

let circle: Circle = {
  color: "Red",
  radius: 10
};

console.log(circle.color); // 输出:Red
console.log(circle.radius); // 输出:10

在这个示例中,我们定义了一个 Shape 接口,它表示一个具有 color 属性的形状。然后,我们定义了一个 Circle 接口,并使用 extends 关键字将它继承自 Shape 接口,这样 Circle 就具有了 color 属性和 radius 属性。最后,我们创建了一个 circle 对象,并输出它的 color 属性和 radius 属性。

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

function createCounter(): Counter {
  let counter = function(start: number) {} as Counter;
  counter.interval = 10;
  counter.reset = function() {};
  return counter;
}

let c = createCounter();
c(10);
c.reset();
c.interval = 5.0;

四、泛型

1. 泛型是什么?有什么作用

当我们定义一个变量不确定类型的时候有两种解决方式:

  • 使用any
    使用any定义时存在的问题:虽然 以 知道传入值的类型但是无法获取函数返回值的类型;另外也失去了ts类型保护的优势
  • 使用泛型
    泛型指的是在定义函数/接口/类型时,不预先指定具体的类型,而是在使用的时候在指定类型限制的一种特性。

2. 在函数中使用泛型

function test <T> (arg:T):T{
  console.log(arg);
  return arg;
}
test<number>(111);// 返回值是number类型的 111
test<string | boolean>('hahaha')//返回值是string类型的 hahaha
test<string | boolean>(true);//返回值是布尔类型的 true

使用方式类似于函数传参,传什么数据类型,T就表示什么数据类型, 使用表示,T也可以换成任意字符串。

3. 在接口中使用泛型

// 注意,这里写法是定义的方法哦
interface Search {
  <T,Y>(name:T,age:Y):T
}

let fn:Search = function <T, Y>(name: T, id:Y):T {
  console.log(name, id)
  return name;
}
fn('li',11);//编译器会自动识别传入的参数,将传入的参数的类型认为是泛型指定的类型

4. 在类中使用泛型

class Animal<T> {
 name:T;
 constructor(name: T){
  this.name = name;
 }
 action<T>(say:T) {
   console.log(say)
 }
}
let cat = new Animal('cat');
cat.action('mimi')

5. 泛型约束

使用接口约束泛型

interface Person {
  name:string;
  age:number;
}
function student<T extends Person>(arg:T):T {
  return arg;
}

student({name:'lili'});//类型 "{ name: string; }" 中缺少属性 "age",但类型 "Person" 中需要该属性
student({ name: "lili" , age:'11'});//不能将类型“string”分配给类型“number”
student({ name: "lili" , age:11});

** 数组泛型**

let arr:Array<number> =[1,2,3] === let arr:number[]=[1,2,3]

6. 泛型工具类型

Partial

partial的作用就是将某个类型中的属性全部变为可选项?

interface Person {
  name:string;
  age:number;
}
function student<T extends Person>(arg: Partial<T>):Partial<T> {
  return arg;
}

Record

Record<K extends keyof any, T>的作用是将K中所有的属性转换为T类型;示例:

interface PageInfo {
  title: string
}
type Page = 'home'|'about'|'other';
const x: Record<Page, PageInfo> = {
  home: { title: "xxx" },
  about: { title: "aaa" },
  other: { title: "ccc" },
};

Pick

Pick<T, K extends keyof T>的作用是将某个类型中的子属性挑出来,变成包含这个类型部分属性的子类型,示例:

interface Todo {
  title:string,
  desc:string,
  time:string
}
type TodoPreview = Pick<Todo, 'title'|'time'>;
const todo: TodoPreview ={
  title:'吃饭',
  time:'明天'
}

Exclude

Exclude<T,U>的作用是将某个类型中属于另一个类型的属性移除掉,示例:

type T0 = Exclude<"a" | "b" | "c", "a">; // "b" | "c"
const t:T0 ='b';

ReturnType

returnType的作用是用于获取函数T的返回类型,示例:

type T0 = ReturnType<() => string>; // string
type T1 = ReturnType<(s: string) => void>; // void
type T2 = ReturnType<<T>() => T>; // {}
type T3 = ReturnType<<T extends U, U extends number[]>() => T>; // number[]
type T4 = ReturnType<any>; // any
type T5 = ReturnType<never>; // any
type T6 = ReturnType<string>; // Error
type T7 = ReturnType<Function>; // Error

六、类(class)

简介:要创建对象,必须先定义类,所谓的类可以理解为对象的模型。举例说明:可以通过Person类来创建人类的对象,通过Dog类来创建狗的对象等等,不同的类用来创建不同的对象。

Person类

// 使用class关键字定义一个类

/**
 * 对象中包含了两个部分:
 *      属性
 *      方法
 * 
 */
class Person {
    // 定义实例属性
    name: string = '猴子';
    age: number = 18;
    // 静态属性
    static num: number = 1;
    // readonly
    readonly gender: string = '男';
    static readonly say: string = 'hello';
    
    // 定义方法
    /**
     * 
     * 通过static定义类方法 可以直接用类去调用
    */
    syaHello() {
        console.log('hello, 大家好');
    }
}
const son = new Person();
son.syaHello(); // hello, 大家好

console.log(son);
console.log(son.age, son.name);

console.log(son.age, son.name, son.num); // 报错 没有num这个属性

son.gender = '女'; // 报错 因为readonly是只读的属性 不可修改
Person.say = '1212'; // 使用static开头且只读的属性 不可修改
Person.num; // 使用static开头的属性是静态属性(类属性),可以直接通过类去调用

dog类

class Dog{
    name: string;
    age: number;

    // constructor 构造函数 构造函数会在对象创建时调用
    constructor(name: string, age: number) {
        // 在实例方法中 this表示当前的实例
        // 可以通过this向新建的对象中添加属性
        this.name = name;
        this.age = age;
    }

    brak() {
        // 在方法中可以用this表示当前调用方法的对象
        //console.log('汪汪汪');
        console.log(this.name);
    }
}

const dog1 = new Dog('小黑', 10);
const dog2 = new Dog('小白', 11);

console.log(dog1); // {name: "小黑", age: 10}
console.log(dog2); // {name: "小白", age: 11}

dog1.brak(); // 小黑

2、继承

(function() {

    // 定义animal类
    class Animal {
        name: string;
        age: number;

        // constructor 构造函数 构造函数会在对象创建时调用
        constructor(name: string, age: number) {
            // 在实例方法中 this表示当前的实例
            // 可以通过this向新建的对象中添加属性
            this.name = name;
            this.age = age;
        }

        sayHello() {
            console.log('动物在叫~');
        }
    }

    /**
     * 此时 Animal 被称为为父类 Dog、Cat 被称为子类
     * 使用继承后 子类将会拥有父类的所有方法和属性
     * 如果在子类中添加了和父类的相同的方法 则子类方法会覆盖父类的方法
    */
    // 定义狗的类 继承Animal类
    class Dog extends Animal {
        run() {
            console.log(`${this.name}在跑路~~~`);
        }
        sayHello() {
            console.log('汪汪汪');
        }
    }

    // 定义猫的类 继承Animal类
    class Cat extends Animal {
        
    }

    const dog = new Dog('小黑', 3);
    const cat = new Dog('咪咪', 2);

    console.log(dog); // Dog {name: "小黑", age: 3}
    dog.sayHello(); // 汪汪汪
    dog.run(); // 小黑在跑路~~~

    console.log(cat); //Dog {name: "咪咪", age: 2}
    cat.sayHello(); // 动物在叫~
    cat.run(); // 报错




})();

3、super

// 超类
(function() {

    // 定义animal类
    class Animal {
        name: string;

        constructor(name: string) {
            this.name = name;
        }

        sayHello() {
            console.log('动物在叫~');
        }
    }

   
    class Dog extends Animal {

        age: number;

        constructor(name: string, age: number) {
            // 如果在子类中写了构造函数 在子类函数中必须对父类的构造函数进行调用
            super(name); // 调用父类的构造函数
            this.age = age;
        }

        sayHello() {
            // 在类的方法中 super表示当前类的父类
            //super.sayHello();
            console.log('汪汪汪');
        }
    }

    const dog = new Dog('小黑', 2);

    dog.sayHello(); // 汪汪汪
    console.log(dog); // Dog {name: "小黑", age: 2}
    


})();

4、抽象类

(function() {

    // 定义animal类
    /**
     * 以abstract开头的类是抽象类
     * 抽象类与其他类区别不大 只是不能用来创建对象
     * 抽象类就是专门用来继承的类
     * 
     * 抽象类中可以添加抽象方法
    */
    abstract class Animal {
        name: string;

        constructor(name: string) {
            this.name = name;
        }

        // 抽象方法使用abstract开头 没有方法体
        // 抽象方法只能定义在抽象类中 子类必须对起抽象方法重写
        abstract sayHello(): void;
    }

   
    class Dog extends Animal {

        sayHello() {
            // 在类的方法中 super表示当前类的父类
            //super.sayHello();
            console.log('汪汪汪');
        }
    }

    class Cat extends Animal {

    } // 报错 没有实现s对ayHello重写

    const dog = new Dog('小黑');

    dog.sayHello();
    console.log(dog);
    
    //const ani = new Animal(); // 报错 无法创建实例


})();
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值