TypeScript简单入门

第一天

1 初识typescript

概述:TypeScript 是一种由微软开发的自由和开源的编程语言。它是 JavaScript 的一个超集,而且本质上向这个语言添加了可选的静态类型和基于类的面向对象编程。

理解:你可以把它当做一门新的编程语言,重新认识它如何定义变量、声明类型而不是单单理解成只有类型提示的作用

1.1 首先安装全局typescript

 npm install -g typescript

一切都从万恶的hello world开始

hello.ts

const hello =(name:string) => {
    return `hello ${name}`
}
hello('world')

1.2 编译 TypeScript 文件

在终端中输入tsc hello.ts命令

$ tsc hello.ts

编译成js
会生成一个hello.js文件

var hello = function (name) {
    return "hello " + name;
};
hello('world');

1.3 在vscode中直接运行typescript

首先全局安装ts-node

npm install ts-node -g

然后安装code runner插件
在这里插入图片描述
点击右上角箭头即可看到结果

2 TypeScript 基础类型

2.1 Boolean 类型

let isDone: boolean = false;
// ES5:var isDone = false;

2.2 Number 类型

let count: number = 20;
// ES5:var count = 20;

2.3 String 类型

let str: string = "hello";
// ES5:var str = 'hello';

2.4 Undefined类型

let u: undefined= undefined;
// ES5:var u = undefined;

2.5 null类型

let nu: null = null;
// ES5:var nu = null;

2.6 any类型和联合类型

在 TypeScript 中,任何类型都可以被归为 any 类型。这让 any 类型成为了类型系统的顶级类型(也被称作全局超级类型)
在许多场景下,这太宽松了。使用 any 类型,可以很容易地编写类型正确但在运行时有问题的代码。如果我们使用 any 类型,就无法使用 TypeScript 提供的大量的保护机制。为了解决 any 带来的问题,TypeScript 3.0 引入了 unknown 类型

//以下都不会报错
//any
let notSure: any = 4;
notSure = 'maybe it is a string';
notSure = true;

notSure.myName
notSure.getName()
notSure.trim(); 
notSure(); 
notSure[0][1]; 

//联合类型
let numberOrString: number | string = 245
numberOrString = 'abc'

2.6 Unknown 类型

就像所有类型都可以赋值给 any,所有类型也都可以赋值给 unknown。这使得 unknown 成为 TypeScript 类型系统的另一种顶级类型(另一种是 any),使用Unkown代替any

区别:unknown 类型要安全得多,因为它迫使我们执行额外的类型检查来对变量执行操作。
示例

let value: unknown;

value = 10; // OK
value = "Hello World"; // OK
value = true; // OK
value = []; // OK
value = {}; // OK
value = Math.random; // OK
value = null; // OK
value = undefined; // OK
value = new TypeError(); // OK

以上对 value 变量的所有赋值都是类型正确的。

如下所示,unknown 类型只能被赋值给 any 类型和 unknown 类型本身。
在这里插入图片描述
在这里插入图片描述

2.7 Array类型

//Array
let array: number[] = [1,2,3,4]
array.push(5)

2.8 Tuple 类型

在 JavaScript 中是没有元组的,元组是 TypeScript 中特有的类型,其工作方式类似于数组。可以包括各种类型

使用元组时,必须提供每个属性的值。

let tuple:[string,number] = ["a",1]
tuple = ['a',1,true]//error

如上,定义了一个 tuple 的变量,它的类型是一个类型数组 [string, number],并按照正确的类型初始化 tuple 变量。与数组一样,也可以通过下标来访问元组中的元素:

console.log(tuple[0]); // a
console.log(tuple[1]); //1

代码在元组初始化的时候,如果出现类型不匹配的话,比如:
tuple = [true, “aa”];
代码此时,TypeScript 编译器会提示以下错误信息:

Type 'boolean' is not assignable to type 'string'.
Type 'string' is not assignable to type 'number'.

很明显是因为类型不匹配导致的。在元组初始化的时候,我们还必须提供每个属性的值,不然也会出现错误,如:

tuple= ["aa"];

运行后TypeScript 编译器会提示以下错误信息:

Type '[string]' is not assignable to type '[string, number]'.
  Source has 1 element(s) but target requires 2.

2.9 Enum 类型

使用枚举可以定义一些带名字的常量。 使用枚举可以清晰地表达意图或创建一组有区别的用例。 TypeScript 支持数字的和基于字符串的枚举
实际上,当我们在声明 enum Direction 时,除了会创建一个名为 Direction 的枚举变量之外,同时也创建了一个名为 Direction 的类型

枚举类型

  1. 数字枚举
  2. 字符串枚举
  3. 异构枚举
2.9.1数字枚举
// 默认情况下,a 的初始值为 0,其余的成员会从 1 开始自动增长。
// 即Direction.b 的值为 1,
// Direction.c 的值为 2,
// Direction.d 的值为 3。
enum Direction {
    a,
    b,
    c,
    d,
  }
  let dir: Direction = Direction.a;
2.9.2 字符串枚举
enum Direction {
    a = "a",
    b = "b",
    c = "c",
    d = "d",
  }
2.9.3 异构枚举

简单来说异构枚举就是枚举值中成员值既有数字类型又有字符串类型

enum Direction {
    A,
    B,
    C = "C",
    D = "D",
    E = 2,
    F,
  }
console.log(Direction.A) //0
console.log(Direction[0]) // A

2.10 Void类型

void 和 any 相反,any 是表示任意类型,而 void 是表示没有任意类型,就是什么类型都不是,这在我们定义函数,函数没有返回值时会用到

// 声明函数返回值为void
function voidTest(): void {
    console.log("void test");
  }
  console.log(typeof voidTest()); //undefined

声明一个 void 类型的变量它的值只能为 undefined 或 null:

2.11 Never类型

never 类型表示的是那些永不存在的值的类型。 例如,never 类型是那些总是会抛出异常或根本就不会有返回值的函数表达式或箭头函数表达式的返回值类型

let foo: never; // 定义never类型的变量

// 返回never的函数必须存在无法达到的终点
function error(message: string): never {
  throw new Error(message);
}

function initLoop(): never {
  while (true) {}
}

never 是所有类型的子类型,因此可以理解为:所有的函数的返回值都包含 never 类型:

function fun(s: string): number {
  if (s == 'a') return 1;
  if (s == 'b') return 2;
  throw new Error;
}
// 等同于 fun(s: string): number | never

然而,没有类型是 never 的子类型或可以赋值给 never 类型(除了 never 本身之外)。 即使 any 也不可以赋值给 never。

let baz: never = 123; // 赋值失败,number类型不能赋值给never类型的变量

// 定义never类型变量,接收返回值类型为never类型的函数返回值
let bar: never = (() => {
  throw new Error('TypeScript never');
})();

never 类型的用途
在 TypeScript 中,可以利用 never 类型的特性来实现详细的检查,具体示例如下:

type Foo = string | number;

function controlFlowAnalysisWithNever(foo: Foo) {
  if(typeof foo === "string") {
    // 这里 foo 被收窄为 string 类型
  } else if(typeof foo === "number") {
    // 这里 foo 被收窄为 number 类型
  } else {
    // foo 在这里是 never
    const check: never = foo;
  }
}

注意在 else 分支里面,我们把收窄为 never 的 foo 赋值给一个显示声明的 never 变量。如果一切逻辑正确,那么这里应该能够编译通过。但是假如后来有一天你的同事修改了 Foo 的类型:

type Foo = string | number | boolean;

然而他忘记同时修改 controlFlowAnalysisWithNever 方法中的控制流程,这时候 else 分支的 foo 类型会被收窄为 boolean 类型,导致无法赋值给 never 类型,这时就会产生一个编译错误。通过这个方式,我们可以确保
controlFlowAnalysisWithNever 方法总是穷尽了 Foo 的所有可能类型。 通过这个示例,我们可以得出一个结论:使用 never 避免出现新增了联合类型没有对应的实现,目的就是写出类型绝对安全的代码。

第二天

3 Interface接口

在面向对象语言中,接口(Interfaces)是一个很重要的概念,它是对行为的抽象,而具体如何行动需要由类(classes)去实现(implement)。

TypeScript 中的接口是一个非常灵活的概念,除了可用于对类的一部分行为进行抽象以外,也常用于对「对象的形状(Shape)」进行描述。

示例

//定义了一个接口 Person(接口一般首字母大写)
interface Person{
    name:string;
    age:number;
}
// 接着定义了一个变量 viking,它的类型是 Person。
// 这样,我们就约束了 viking 的形状必须和接口 Person 一致。
let viking:Person = {
    name:'viking',
    age:18
}

定义的变量比接口少了或多了一些属性都是不允许的:

interface Person{
    name:string;
    age:number;
}
let viking:Person = {
    name:'viking',
}
//Property 'age' is missing in type '{ name: string; }' but required in type 'Person'.
interface Person{
    name:string;
    age:number;
}
let viking:Person = {
    name:'viking',
    age:18,
    gender: 'male'
}
//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 viking:Person = {
    name:'viking',
}

只读属性
有时候我们希望对象中的一些字段只能在创建的时候被赋值,那么可以用 readonly 定义只读属性:

interface Person{
    readonly id:number;//只读属性
    name:string;
    age?:number; //可选属性
}
let viking:Person = {
    id:1,
    name:'viking',
}
viking.id = 2 //Cannot assign to 'id' because it is a read-only property.

4 函数类型

4.1 函数声明和函数表达式

//函数声明
function add(x:number,y:number):number{
    return x + y
}

let res = add(1,2)
console.log(res) //3

//函数表达式
let add = function(x:number,y:number):number{
        return x + y
}

这是可以通过编译的,不过事实上,上面的代码只对等号右侧的匿名函数进行了类型定义,而等号左边的 add ,是通过赋值操作进行类型推论而推断出来的。如果需要我们手动给 add 添加类型,则应该是这样:

let add : (x: number, y: number) => number = function (x: number, y: number): number {
    return x + y;
};

同样,输入多余的(或者少于要求的)参数,也是不被允许的。

4.2 可选参数

可选参数必须接在必需参数后面。


let add = function(x:number,y:number,z?:number):number{
    if(typeof z === 'number'){
        return x + y + z //z可选参数
    }else{
        return x + y
    }
}
console.log(add(1,2,3)) //6

4.3 参数默认值

TypeScript 会将添加了默认值的参数识别为可选参数:

let add = function(x:number,y:number,z:number = 4):number{
        return x + y + z
}
console.log(add(1,2,3)) //6

let add = function(x:number,y:number,z:number = 4):number{
        return x + y + z
}
console.log(add(1,2)) //7

4.4 剩余参数

可以使用 …rest 的方式获取函数中的剩余参数(rest 参数)

function push(array, ...items) {
  items.forEach(function (item) {
    array.push(item);
  });
}

let a = [];
push(a, 1, 2, 3);

4.5 函数重载

要求定义一系列的函数声明,在类型最宽泛的版本中实现重载, TS 编译器的函数重载会去查询一个重载的列表,并且从最开始的一个进行匹配,如果匹配成功,就直接执行。所以我们要把大概率匹配的定义写在前面

function add(a: number, b: number): number;
function add(a: string, b: string): string;
function add(a: string, b: number): string;
function add(a: number, b: string): string;
function add(a:string | number, b:string | number):string | number | void{
  if (typeof a === "string" || typeof b === "string") {
    return a.toString() + b.toString();
  }
  return a + b;
}
console.log(add(1, 2)) //3
console.log(add("a", "b")) //ab
console.log(add("a", 2)) //a2

如上所示,给 add 函数提供了多个函数类型定义,从而实现函数的重载。在 TypeScript 中除了可以重载普通函数之外,我们还可以重载类中的成员方法。

注意,当 TypeScript 编译器处理函数重载时,它会查找重载列表,尝试使用第一个重载定义。 如果匹配的话就使用这个。 因此,在定义重载的时候,一定要把最精确的定义放在最前面。另外,function add(a:string | number, b:string | number):string | number | void 并不是重载列表的一部分,因此对于 add 成员方法来说,我们只定义了四个重载方法。

5 类型断言

概述:
有时候你会遇到这样的情况,你会比 TypeScript 更了解某个值的详细信息。通常这会发生在你清楚地知道一个实体具有比它现有类型更确切的类型
类型断言(Type Assertion)可以用来手动指定一个值的类型。

语法

  1. as语法 :值 as 类型
  2. 尖括号语法 : <类型>值

在 tsx 语法(React 的 jsx 语法的 ts 版)中必须使用前者,即 值 as 类型。

形如 的语法在 tsx 中表示的是一个 ReactNode,在 ts 中除了表示类型断言之外,也可能是表示一个泛型。
故建议使用 值 as 类型 这样的语法,在这里插入图片描述

6 类

6.1类的概念

类(Class):定义了一件事物的抽象特点,包含它的属性和方法
对象(Object):类的实例,通过 new 生成
面向对象(OOP)的三大特性:封装、继承、多态
简单示例

class Animal {
    name:string;
    constructor(name:string){
        this.name = name
    }
    run(){
        return `${this.name} is runing`
    }
}
//通过 new 生成新实例的时候,会自动调用构造函数
const snake = new Animal('lily')
console.log(snake.run()) //lily is runing

6.2 类的继承

使用 extends 关键字实现继承,子类中使用 super 关键字来调用父类的构造函数和方法。

class Animal {
    name:string;
    constructor(name:string){
        this.name = name
    }
    run(){
        return `${this.name} is runing`
    }
}

const snake = new Animal('lily')
console.log(snake.run()) //lily is runing

class Dog extends Animal{
    bark(){
        return `${this.name} is barking`
    }
}

const xiaobao = new Dog('xiaobao')
console.log(xiaobao.run())
console.log(xiaobao.bark())

6.3 重写(多态)

class Animal {
    name:string;
    constructor(name:string){
        this.name = name
    }
    run(){
        return `${this.name} is runing`
    }
}

const snake = new Animal('lily')
console.log(snake.run()) //lily is runing

class Cat extends Animal{
    constructor(name:string){
        super(name)
        console.log(this.name)
    }
    run(){
        return 'Meow,' + super.run()
    }
}

const maomao = new Cat('maomao')
console.log(maomao.run())

6.4 修饰符(public,private,protected)

  1. public 修饰的属性或方法是公有的,可以在任何地方被访问到,默认所有的属性和方法都是 public 的
  2. private修饰的属性或方法是私有的,不能在声明它的类的外部访问
  3. protected 修饰的属性或方法是受保护的,它和 private类似,区别是它在子类中也是允许被访问的
class Animal {
   public name:string;
    constructor(name:string){
        this.name = name
    }
    run(){
        return `${this.name} is runing`
    }
}

const snake = new Animal('lily')
console.log(snake.name) //lily 
snake.name = 'lucy'
console.log(snake.name) //lucy

private
在这里插入图片描述
protected ,子类中允许被访问
在这里插入图片描述
readonly

在这里插入图片描述

6.5 static静态

ES6 中实例的属性只能通过构造函数中的 this.xxx 来定义,ES7 提案中可以直接在类里面定义:
使用时不需要实例化,可以直接调用

class Animal {
    name:string;
   static categoies:string[] = ['mammal','bird'];
   static isAnimal(a:any){
       return a instanceof Animal
   }
    constructor(name:string){
        this.name = name
    }
    run(){
        return `${this.name} is runing`
    }
}
console.log(Animal.categoies) //[ 'mammal', 'bird' ]
const snake = new Animal('lily')
console.log(Animal.isAnimal(snake)) //true

6.6 存取器

TypeScript支持通过getters/setters来截取对对象成员的访问。 它能帮助你有效的控制对对象成员的访问。

let passcode = "Hello World";

class Employee {
  private _fullName: string;

  get fullName(): string {
    return this._fullName;
  }

  set fullName(newName: string) {
    if (passcode && passcode == "Hello World") {
      this._fullName = newName;
    } else {
      console.log("Error: Unauthorized update of employee!");
    }
  }
}

let employee = new Employee();
employee.fullName = "lisa";
if (employee.fullName) {
  console.log(employee.fullName);
}

7 泛型

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

7.1 简单示例

function echo<T>(arg:T): T {
    return arg
}

const str: string = 'str'
const result = echo(str)

我们给echo添加了类型变量T。 T帮助我们捕获用户传入的类型(比如:number),之后我们就可以使用这个类型。 之后我们再次使用了 T当做返回值类型。现在我们可以知道参数类型与返回值类型是相同的了。 这允许我们跟踪函数里使用的类型的信息。

我们把这个版本的echo函数叫做泛型,因为它可以适用于多个类型。 不同于使用 any,它不会丢失信息,像第一个例子那像保持准确性,传入数值类型并返回数值类型。

7.2 多个类型参数

定义泛型的时候,可以一次定义多个类型参数:

//定义一个 swap 函数,用来交换输入的元组。
function swap<T,U>(tuple:[T,U]): [U,T]{
    return [tuple[1],tuple[0]]
}

const result2 = swap(['string',123])

7.3 泛型约束

function echoWithArr1<T>(arg: T): T {
    // 泛型 T 不一定包含属性 length,所以编译的时候报错了。
    // /Property 'length' does not exist on type 'T'
    console.log(arg.length);
    return arg
}

//缺点:只能传入数组
function echoWithArr<T>(arg: T[]): T[] {
    console.log(arg.length);
    return arg
}
const arrs = echoWithArr([1,2,3])

//泛型约束

interface IWithLength{
    length:number
}
//T extends IWithLength  
//我们使用了 extends 约束了泛型 T 必须符合接口 IWithLength 的形状,也就是必须包含 length 属性。
function echoWithLength<T extends IWithLength>(arg: T): T {
    console.log(arg.length);
    return arg
}
const str = echoWithLength('str')
const obj = echoWithLength({length:10})
const arr2 = echoWithLength([1,2,3])

7.4 泛型——类和接口

class Queue<T> {
    private data = [];
    push(item: T){
        return this.data.push(item)
    }
    pop(): T{
        return this.data.shift()
    }
}
const queue = new Queue<number>()
queue.push(1)
console.log(queue.pop().toFixed())

const queue2 = new Queue<string>()
queue2.push('str')
console.log(queue2.pop().length)


interface KeyPair<T,U>{
    key:T,
    value:U
}

let kp1: KeyPair<number,string> = {key:1,value:'str'}
let kp2: KeyPair<string,number> = {key:'test',value:2}

let arr: number[] = [1,2,3]
let arrTwo: Array<number> = [1,2,3]
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值