ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

TypeScript的学习

2022-04-25 18:34:27  阅读:158  来源: 互联网

标签:TypeScript name age number 学习 let 类型 string


1.TypeScript简介

首先官网祭天 ---> https://www.tslang.cn/

TypeScript 是 JavaScript 的一个超集,支持 ECMAScript 6 标准。

TypeScript 由微软开发的自由和开源的编程语言。

TypeScript 设计目标是开发大型应用,它可以编译成纯 JavaScript,编译出来的 JavaScript 可以运行在任何浏览器上。

我们使用一张图来说明TypeScript和JavaScript的关系:

 

使用更加简单的说法:

TypeScript = JavaScript + 类型约束 + 高级特性

 

二.环境准备

TypeScript最终会编译成js来运行,所以我们需要先准备好将TypeScript编译为js的环境。

a.安装TypeScript

首先全局安装ts

npm install -g typescript

然后可以使用命令来验证安装是否成功了。

tsc --version

b.使用tsc命令编译ts为js

tsc ./src/xxx.ts

然后我们就可以执行对应的js文件

c.配置tsconfig文件

但是每次都使用tsc命令编译单个文件会比较麻烦,所以我们可以做一个统一配置

先使用 tsc --init 命令初始化一个 tsconfig.json 文件

里面配置如下:

复制代码
{
  "compilerOptions": {
    "target": "ESNext",
    "module": "ESNext",
    "outDir": "./dist",
    "skipLibCheck": true,
     ...
  }
}
复制代码

然后我们只需要tsc一下,所有的ts文件就能被编译了

d.自动编译

之后我们只需要启动vscode的监听任务功能,就能自动编译了。

1.选择终端 -> 运行任务

2.选择typescript类型的任务

3.选择监视任务

 

三.变量类型约束

3.1.原始类型

string,number,boolean,symbol,null,undefined

复制代码
export {}; // 第一行增加这个是为了使文件里的变量不污染全局
let num: number = 1; // number
let str: string = "2"; // string
let bool: boolean = true; // boolean
let sy: symbol = Symbol(); // symbol
let undef: undefined = undefined; // undefined
let nul:null = null; // null
let vd: void = undefined; // 可以把undefined类型赋值给void类型,但是反过来不行
// 函数没有返回值,那么函数的返回值类型就是void
function fn(): void {
  return undefined;
}
复制代码

注意:

  1. void只用在函数没有返回值的情形下。

  2. undefined和null最大的价值主要体现在接口类型上,表示可缺省、未定义的属性;null表示对象或者属性是空值。这个可以先有个印象,后面说到接口会讲

  3. 单纯声明 undefined 或者 null 类型的变量是无比鸡肋的,上面的例子只是说明原始类型

  4. 如果不写类型,typescript是可以推断类型的,但注意let、const的区别

 

3.2.非原始类型

小object, 大Object, {}

小object:代表的是非原始类型的类型,也就是不能是string,number,boolean,symbol,严格模式:多包括null,undefined

复制代码
let obj1: object = 3; // 报错
let obj2: object = "3"; // 报错
let obj3: object = true; // 报错
let obj4: object = null; // 报错
let obj5: object = undefined; // 报错
let obj6: object = Symbol(); // 报错
let obj7:object = {a: 1, b: '2'};
let obj8:object = [1, 2, 3];
复制代码

大Object :代表所有拥有 toString、hasOwnProperty 方法的类型,所以所有原始类型、非原始类型都可以赋给 Object,严格模式下不包括null,undefined。{}空对象类型和大 Object 一样。

复制代码
let obj1: Object = 3; 
let obj2: Object = "3"; 
let obj6: Object = Symbol(); 
let obj3: Object = true; 
let obj4: Object = null; // 报错
let obj5: Object = undefined; // 报错
let obj7: Object = { a: 1, b: "2" };
let obj8: Object = [1, 2, 3];
复制代码

注意: 1. 官方文档说可以使用小 object 代替大 Object,但是我们仍要明白大 Object 并不完全等价于小 object。 2. 上面的例子看起来,大Object是小object的父类型,但并不是!!!真实的情况是大Object才是小object的子类型

 

3.3.数组类型

数组类型的定义:

let arr1: Array<number> = [1, 2, 3];
arr1.push('3'); // 报错
arr1.push(5);
let arr2: string[] = ['4', '5', 'a'];
arr2[3] = '6';

 

3.4.字面量类型和联合类型和交叉类型

复制代码
export {};

// 字面量类型
let num: 2 = 2;
// num = 3;

let str: "hello";
str = "hello";
// str = "123";

let bool: true;
// bool = false;//报错

// 联合类型 a | b
let num1: 2 | "3";
num1 = 2;
num1 = "3";
// num1 = true;//报错

// 交叉类型 a & b
let str1: string & number;

let obj: { name: string } & { age: number };
obj = {
  name: "张三",
  age: 20,
};

// 联合类型|要比交叉类型&优先级要低
let obj1:
  | ({ id: number } & { name: string })
  | ({ id: string } & { age: number });
obj1 = {
  id: 1,
  name: "",
};
obj1 = {
  id: "",
  age: 1,
};
复制代码

 

3.5.any类型和unknown类型

复制代码
export {};

// any: 任意类型, 跳过类型检测
// unknown: 未知, Typescript3.0,描述类型不确定的变量,它会进行类型检测

let num: any = 1;
num.toFixed(2);

let num1: unknown;

if (true) {
  num1 = 1;
}
// num.a=1; //报错
(num1 as {a:number}).a=3

// 类型缩小
if (typeof num1 === "number") {
  num1.toFixed(2);
}

// 类型缩小
(num1 as number).toFixed(2);//断言
复制代码

 

3.6.never类型

never表示永远不会发生值的类型

function throwErrFn():never {
    throw new Error('出错了');
}

注意:

  1. 如果函数里是死循环,返回值类型也是never

  2. never 是所有类型的子类型

 

四.接口

TypeScript 不仅能帮助前端改变思维方式,还能强化面向接口编程的思维和能力,而这正是得益于 Interface 接口类型。

4.1.定义变量和函数的类型

使用接口定义变量和函数参数的类型

复制代码
interface PersonInfo {
  name: string;
  age: number;
}

// 定义变量的类型
let zhangsan: PersonInfo = {
  name: "张三",
  age: 20,
};

// 定义数组的类型
interface ArrayNumber {
  [idx: number]: number;
}

let arr1: ArrayNumber = [1, 2, 3];

// 定义函数的类型
interface PersonFn {
  (p: PersonInfo): void;
}

let Person1: PersonFn = (obj)=> {
  console.log(obj.name, obj.age);
};
复制代码

注意:

  1. 很少使用接口类型来定义函数的类型,更多使用内联类型或类型别名配合箭头函数语法来定义函数类型;

 

4.2.继承

多个不同接口之间是可以实现继承的,但是如果继承的接口PersonInfo和被继承的接口NameInfo有相同的属性,并且类型不兼容,那么就会报错。

 

复制代码
interface NameInfo {
  name: string;
}

interface AgeInfo {
  age: number;
}

interface PersonInfo extends NameInfo, AgeInfo {
  height: number;
}

let zs: PersonInfo = {
  name: "张三",
  age: 20,
  height: 177,
};
复制代码

多个不同的接口可以实现继承,组合成一个新的接口,那么如果出现多个相同名字的接口会怎么样?

 

4.3. 多个相同接口

多个相同名字的接口,会进行合并,得到一个新的接口;这个接口的特性一般用在扩展第三方库的接口类型。

复制代码
interface PersonInfo {
    name: string,
    age: number
}

interface PersonInfo {
    name: string,
    height: number
}

let zs: PersonInfo = {
  name: "张三",
  age: 20,
  height: 177,
};
复制代码

 

4.4. 缺省和只读特性

interface PersonInfo {
  name?: string; // 缺省
  readonly height: number; // 只读
}

 

 

五.类型别名

接口类型的一个作用是将内联类型抽离出来,从而实现类型可复用。其实,我们也可以使用类型别名接收抽离出来的内联类型实现复用。格式:type 别名名称 = 类型定义。

复制代码
export {};
// type 类型名称 = 类型定义

type ArrType<V> = { [index: number]: V };
let arr: ArrType<number> = [1, 2];

type ObjType = { name?: string; age?: number };

let obj: ObjType = {
  name: "",
  age: 1,
};

// 类型别名不能重名
// type ObjType

// 类型别名是可以直接使用交叉类型和联合类型,接口类型不能
type Type1 = number | string | boolean;
type Type2 = ArrType<number> & ObjType;

// 定义函数
type FnType = (num: number, str: string) => 1;
let fn: FnType = function (num, str) {
  return 1;
};

// Required是Typescript自带的工具类型
type ReType = Required<ObjType>;
复制代码

 Interface 与 Type 的区别

实际上,在大多数的情况下使用接口类型和类型别名的效果等价,但是在某些特定的场景下这两者还是存在很大区别。

  1. 重复定义的接口类型,它的属性会叠加,这个特性使得我们可以极其方便地对全局变量、第三方库的类型做扩展

  2. 如果我们重复定义类型别名,那么就会报错

 

六.函数类型

6.1. 基础定义

显式指定函数参数和返回值的类型

const add = (a: number, b: number): number => {
     return a + b;
}

或者用type来声明函数类型

type addFnType = (a: number, b:number) => number;
let addFn: addFnType = (num1, num2) => {
    return num1 + num2;
}

6.2. 函数参数类型

参数一般有:可选参数、默认参数、剩余参数;

1. 可选参数

在类型标注的:前添加?表示 log 函数的参数 x 就是可缺省的;

function log(msg?: string):void {
    console.log(msg);
}

可缺省是不是相当于msg参数的类型就是和string | undefined等价呢?这个当然不是,string | undefined的意思是这两个类型中的一种,而可缺省是不传的意思。

2. 默认参数

function addFn1(num1: number = 1, num2: number = 2):number {
    return num1 + num2;
}

3. 剩余参数

function sum(...nums: number[]) {
    return nums.reduce((a, b) => a + b, 0);
}
sum(1, 2); // => 3
sum(1, 2, 3); // => 6

 

4. this

函数中的this问题,一直都是javascript最令人头疼的问题,因为this的指向只有函数调用的时候才能确定。还有一些可以改变this指向的方法(apply,call,bind)。

但是在Typescript中,必须要明确的指定this的类型(严格模式下)。

复制代码
type objType = {person: (n: string) => void, myname: string};
function person(this: Window | objType , name: string):void {
    this.myname = name;
    console.log(this.myname);
}
window.person = person;
window.person('window name');
let obj:objType = {
    person,
    myname: ''
};
obj.person('obj name');
复制代码

单单是上面的代码是有问题的,我们还需要创建一个类型声明文件global.d.ts,为window对象上扩展两个属性person、myname;

interface Window {
  person: (n: string) => void;
  myname: string;
}

定义对象的函数属性时,只要实际调用中 this 的指向与指定的 this 指向不同,TypeScript 就能发现 this 指向的错误

复制代码
interface ObjType2 {
    name: string;
    say: (this: ObjType2) => void;
}
let obj2:ObjType2 = {
    name: 'obj2',
    say() {
        console.log(this.name);
    }
} 

obj2.say(); // ok

let t11 = obj2.say;
t11();
复制代码

注意:

  1. 显式声明函数的返回值类型为 undfined,则会出现错误提示,如果没有返回值,我们用void表示;

  2. 注意:显式注解函数中的 this 类型,它表面上占据了第一个形参的位置,但并不意味着函数真的多了一个参数,因为 TypeScript 转译为 JavaScript 后,“伪形参” this 会被抹掉,这算是 TypeScript 为数不多的特有语法。

 

七.枚举

数字枚举和字符串枚举

枚举的作用在于定义被命名的常量集合,一个默认从0 开始递增的数字集合,称之为数字枚举.也可以指定值,这里可以指定的值 可以是数字或者字符串.

复制代码
enum Days {
    Sunday = 1,
    Monday,
    Tuesday,
    Wednesday,
    Thursday,
    Friday,
    Saturday
}
let day = Days.Sunday;
复制代码

 

八.泛型

1. 什么是泛型?

泛型指的是类型参数化,即将原来某种具体的类型进行参数化。设计泛型的目的在于有效约束类型成员之间的关系,比如函数参数和返回值、类或者接口成员和方法之间的关系。

2. 泛型类型参数

复制代码
function getValue(val: string): string {
  return val;
}

function getValue1(val: number): number {
  return val;
}

function getValue2(val: unknown): unknown {
  return val;
}

let g1: string = getValue("1");
let g2: number = getValue1(1);
let g3: unknown = getValue2(1);

function getValue3<T>(val: T): T {
  return val;
}

let g4: number = getValue3<number>(3);
let g5: string = getValue3<string>('4');
复制代码

3. 泛型类型

前面我们使用过Array<类型>来定义数组的类型,这里的Array也是一种类型。

在 TypeScript 中,类型本身就可以被定义为拥有不明确的类型参数的泛型,并且可以接收明确类型作为入参,从而衍生出更具体的类型。

复制代码
// 定义数组类型
let arr: Array<number> = [1];
let arr1: Array<string> = [""];

// 类型别名
type typeFn<P> = (params: P) => P;
let fntype: typeFn<number> = (n: number) => {
  return n;
};

let fn1:typeFn<string> = (p: string):string => {
    return p;
} 
// 定义接口类型
interface TypeItf<P> {
  name: P;
  getName: (p: P) => P;
}

let t1: TypeItf<number> = {
  name: 123,
  getName: (n: number) => {
    return n;
  },
};

let t2: TypeItf<string> = {
  name: "123",
  getName: (n: string) => {
    return n;
  },
};
复制代码

4. 泛型约束

把泛型入参限定在一个相对更明确的集合内,以便对入参进行约束。

复制代码
interface TypeItf<P extends string | number> {
  name: P;
  getName: (p: P) => P;
}

let t1: TypeItf<number> = {
  name: 123,
  getName: (n: number) => {
    return n;
  },
};

let t2: TypeItf<string> = {
  name: "123",
  getName: (n: string) => {
    return n;
  },
};
复制代码

 

九.class(类)

面向对象实践 OOP 编程思想,在实际工作中,都是极其有用的抽象、封装利器。

复制代码
class Person {
  name: string;
  say(this: Person, song: string): Person {
      console.log(song);
      return this;
  }
  constructor(name: string) {
    this.name = name;
  }
}

let p1 = new Person('张三');
p1.say('Song').name;
复制代码

1. 继承

使用extends关键字实现继承

复制代码
class Male extends Person {
    age: number;
    constructor(name: string, age: number) {
        super(name);
        this.age = age;
    }
}
复制代码

2. 修饰符(public、private、protected、readonly)

在 TypeScript 中就支持 3 种访问修饰符,分别是 public、private、protected。通过这三个修饰符做到控制属性和方法的访问。

  • public:基类、子类、类外部都可以访问;

  • protected:基类、子类可以访问,类外部不可以访问;

  • private:基类可以访问,子类、类外部不可以访问;

  • readonly:只读修饰符

复制代码
class Person {
    public readonly name: string = '张三';
    protected age: number = 20;
    private height: string = '180';
    protected getPersonInfo():void {
        console.log(this.name, this.age, this.height); // 基类里面三个修饰符都可以访问
    }
}

class Male extends Person {
    public getInfo():void {
        console.log(this.name, this.age); // 子类只能访问public、protected修饰符的
    }
}

let m = new Male();
console.log(m.name); // 类外部只能访问public修饰的
m.name = '李四'; // name属性使用只读修饰符,所以不能对name进行赋值修改操作
复制代码

3. 静态属性

基于静态属性的特性,往往会把与类相关的常量、不依赖实例 this 上下文的属性和方法定义为静态属性,从而避免数据冗余,进而提升运行性能。

class Person {
  static title: string = "个人信息";
}

Person.title;

4. 抽象类和接口

抽象类,它是一种不能被实例化仅能被子类继承的特殊类。

复制代码
abstract class Person {
  abstract name: string;
  abstract getName(): void;
  extendsFn():void {
    console.log('扩展方法');
  }
}

class Male extends Person {
  constructor(name: string) {
    super();
    this.name = name;
  }
  name: string;
  getName(): void {
    console.log(this.name);
  }
}
复制代码

接口interface也可以约束类的实现,使用接口与使用抽象类相比,区别在于接口只能定义类成员的类型

复制代码
interface Person {
  name: string;
  age: number;
  getName: () => void;
}

class Male implements Person {
  constructor(name: string, age: number) {
    this.name = name;
    this.age = age;
  }
  name: string;
  age: number;
  getName(): void {
    console.log(this.name);
  }
}
复制代码

5. 类的类型

在声明类的时候,其实也同时声明了一个特殊的类型(确切地讲是一个接口类型),这个类型的名字就是类名,表示类实例的类型。

复制代码
class Male {
  constructor(name: string, age: number) {
    this.name = name;
    this.age = age;
  }
  name: string;
  age: number;
  getName(this: Male): void {
    console.log(this.name);
  }
}

let m1: Male = new Male("张三", 20);
let m2: Male = {
  name: "张三",
  age: 20,
  getName(this: Male) {
    console.log(this.name);
  },
};
m2.getName();
let fn = m2.getName;
fn(); // 报错,this指向并不是Male对象
复制代码

 

十一、工具类型

打造自己的工具类型,这个时候需要用到一些物料

1. extends

extends关键字判断泛型参数P是否是string或者是number其中的一种,最终类型的确定由三元运算的结果决定。

type TypeFn<P> = P extends string | number ? P[] : P;
let m: TypeFn<number> = [1, 2, 3];
let m1: TypeFn<string> = ['1', '2', '3'];
let m2: TypeFn<boolean> = true;

2. 类型推断 infer

类型推断infer相当于声明一个变量接收传入的类型

type ObjType<T> = T extends { name: infer N; age: infer A } ? [N, A] : [T];
let p: ObjType<{ name: string; age: number }> = ["张三", 1];
let p1: ObjType<{name: string}> = [{name: '张三'}];

3. keyof

Keyof提取对象属性名、索引名、索引签名的类型;

复制代码
interface NumAndStr {
  name: string;
  age: number;
  [key: number]: string | number;
}
type TypeKey = keyof NumAndStr; // number | 'name' | 'age'
let t:TypeKey = 'name';
复制代码

4. in

in是映射类型

复制代码
type NumAndStr = number | string;
type TargetType = {
  [key in NumAndStr]: string | number;
};

let obj: TargetType = {
    1: '123',
    "name": 123
}
复制代码

注意:

  1. 我们只能在类型别名定义中使用 in,如果在接口(interface)中使用,则会提示一个错误

  2. in 和 keyof 也只能在类型别名定义中组合使用

 

5. typeof

typeof 的主要用途是在类型上下文中获取变量或者属性的类型

复制代码
// 推断变量的类型
let strA = "2";
type KeyOfType = typeof strA; // string

// 反推出对象的类型作为新的类型
let person = {
    name: '张三',
    getName(name: string):void {
        console.log(name);
    }
}
type Person = typeof person;
复制代码

标签:TypeScript,name,age,number,学习,let,类型,string
来源: https://www.cnblogs.com/enen0627/p/16191516.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有