Hypecode Hypecode
首页
  • 前端基础笔记

    • JavaScript文章
    • CSS文章
    • Typescript文章
  • 前端框架笔记

    • Vue3笔记
    • Vue源码(理)笔记
    • echarts使用笔记
  • 前端架构笔记

    • Vite文章
    • pnpm文章
  • Node
  • Git
  • 学习规划
  • 友情链接
  • 面试记录
关于
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

Hypeng

看看远处的雪山吧
首页
  • 前端基础笔记

    • JavaScript文章
    • CSS文章
    • Typescript文章
  • 前端框架笔记

    • Vue3笔记
    • Vue源码(理)笔记
    • echarts使用笔记
  • 前端架构笔记

    • Vite文章
    • pnpm文章
  • Node
  • Git
  • 学习规划
  • 友情链接
  • 面试记录
关于
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • TypeScript 基础
    • 基础概念
      • 静态类型检查(Static type-checking)
      • 非异常失败(Non-exception)
      • 类型工具
      • tsc TypeScript 编译器(tsc,the TypeScript compiler)
      • tsconfig
      • 显式类型
      • 类型抹除
      • 降级(Downleveling)
      • 严格模式
      • noImplicitAny
      • strictNullChecks
    • TS常见类型
      • 原始类型: string,number 和 boolean
      • 数组(Array)
      • 元组
      • Any
      • unknown
      • 枚举
      • 类型注释
      • 函数
      • 对象类型
      • 联合类型
      • 类型别名(Type Aliases)
      • 接口
      • 类型断言
      • 字面量类型(Literal Types)
      • null 和undefined
      • 枚举
      • 不太常用的原语
    • 类型收窄
      • in 操作符收窄
    • 函数
      • 调用签名
      • 泛型函数
  • Typescript面向对象
  • Typescript 学习
Hypeng
2022-08-26
目录

TypeScript 基础

# TypeScript

# 基础概念

# 静态类型检查(Static type-checking)

如下段代码 , 定义一个string类型变量,把它当做函数调用, 如果是js,那么在保存文件重新运行代码时,才会抛出错误。

理想情况下,我们应该有一个工具可以帮助我们,在代码运行之前就找到错误。这就是静态类型检查器比如 TypeScript 做的事情。静态类型系统(Static types systems)描述了值应有的结构和行为。一个像 TypeScript 的类型检查器会利用这个信息,并且在可能会出错的时候告诉我们:

const message = "hello!";
 
message();

// This expression is not callable.
// Type 'String' has no call signatures.

1
2
3
4
5
6
7

# 非异常失败(Non-exception)

JavaScript会告诉我们运行时的错误 运行时错误,就是 JavaScript 会在运行时告诉我们它认为的一些没有意义的事情。这些事情之所以会出现,是因为 ECMAScript 规范 (opens new window) (opens new window)已经明确的声明了这些异常时的行为。

  • 比如在读取一个对象中不存在的属性时,js并不会报错,而是返回undefined

    const user = {
      name: "Daniel",
      age: 26,
    };
    user.location; // returns undefined
    
    
    1
    2
    3
    4
    5
    6

    而在ts中 是这样表现得

    const user = {
      name: "Daniel",
      age: 26,
    };
     
    user.location;
    // Property 'location' does not exist on type '{ name: string; age: number; }'.
    
    
    1
    2
    3
    4
    5
    6
    7
    8
  • Ts可以捕获很多合理的错误 如:

    • 拼写错误

      const announcement = "Hello World!";
       
      // How quickly can you spot the typos?
      announcement.toLocaleLowercase();
      announcement.toLocalLowerCase();
       
      // We probably meant to write this...
      announcement.toLocaleLowerCase();
      
      
      1
      2
      3
      4
      5
      6
      7
      8
      9
    • 定义函数未被调用

      function flipCoin() {
        // Meant to be Math.random()
        return Math.random < 0.5;
      // Operator '<' cannot be applied to types '() => number' and 'number'.
      }
      
      
      1
      2
      3
      4
      5
      6
    • 基本逻辑错误

      const value = Math.random() < 0.5 ? "a" : "b";
      if (value !== "a") {
        // ...
      } else if (value === "b") {
        // This condition will always return 'false' since the types '"a"' and '"b"' have no overlap.
        // Oops, unreachable
      }
      
      
      1
      2
      3
      4
      5
      6
      7
      8

# 类型工具

​ 全局安装ts npm install typescript -g 类型检查器因为有类型信息,可以检查比如说是否正确获取了一个变量的属性。也正是因为有这个信息,它也可以在你输入的时候,列出你可能想要使用的属性。

# tsc TypeScript 编译器(tsc,the TypeScript compiler)

tsc的指令:

  • tsc init : 生成tsconfig.json 配置文件
  • tsc --watch : 实时编译
  • tsc --noEmitOnError hello.ts : 当存在ts报错时 不更新js

# tsconfig

tsconfig.json是ts编译器的配置文件,ts编译器可以根据它的信息来对代码进行编译

具体配置项:

{
  "include":{    
  },
  "compilerOptions":{    
    "target":"es2015", // 用来指定编译的es版本
    "module":"es2015", // 指定要使用的模块化规范
    "lib":[], // 指定项目中要使用的库
    "outDir": './dist', // 编译后的文件指定的目录
    "outFile":'./dist/app.js', // 将代码合并为一个文件 所有的全局作用域中的代码会合并到同一个文件中
    
    "allowJs": true, // 是否对js文件进行编译 默认为false
    "checkJs": true, // 检查js语法是否正确 默认为false
    "removeCommonet":false, // 是否移除注释
		"noEmit":false, // 不生成编译后的文件
    "noEmitOnError":false, // 编译报错不生成编译后的文件
    
    "strict": true, // 所有严格检查的总开关 开了就全开
    "alwaysStrict":true, // 设置编译后的文件是否是严格模式 默认false
    "NoImplictAny":true, // 不允许隐式的any类型
    "NoImplictThis":true, // 不允许不明确类型的this
    "strictNullChecks":true, // 严格的检查空值
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

# 显式类型

定义显式类型

function greet(person: string, date: Date) {
  console.log(`Hello ${person}, today is ${date.toDateString()}!`);
}

1
2
3
4

我们所做的就是给 person 和 date 添加了类型注解(type annotations),描述 greet 函数可以支持传入什么样的值。你可以如此理解这个签名 (signature): greet 支持传入一个 string 类型的 person 和一个 Date 类型的 date

# 类型抹除

类型注解并不是 JavaScript 的一部分。所以并没有任何浏览器或者运行环境可以直接运行 TypeScript 代码。

所以在生成的js文件中我们的类型注解就全部被抹除了。

"use strict";
function greet(person, date) {
    console.log("Hello " + person + ", today is " + date.toDateString() + "!");
}
greet("Maddison", new Date());

1
2
3
4
5
6

# 降级(Downleveling)

Ts再经过tsc的编译后生成了了

"Hello " + person + ", today is " + date.toDateString() + "!";
1

是因为模板字符串是 ECMAScript2015(也被叫做 ECMAScript 6 ,ES2015, ES6 等)里的功能,TypeScript 可将新版本的代码编译为老版本的代码,比如 ECMAScript3 或者 ECMAScript5 。这个将高版本的 ECMAScript 语法转为低版本的过程就叫做降级(downleveling) 。

Ts 默认转换为ES3 ,通过tsconfig target选项可以进行配置 或者用命令行配置target tsc --target es2015 hello.ts

# 严格模式

TypeScript 有几个严格模式设置的开关。

# noImplicitAny

用来检验any

# strictNullChecks

默认情况下,像 null 和 undefined 这样的值可以赋值给其他的类型。可以让我们更方便的写一些代码。但是忘记处理 null 和 undefined 也导致了不少的 bug

# TS常见类型

#

#

# 原始类型: string,number 和 boolean

# 数组(Array)

定义一个类似[1,2,3] 的数组 需要用到语法 number[] 这个语法可以适用于任何类型 比如 string[] 还有一种写法是泛型写法:Array<number>

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

# 元组

元组实际上就是固定长度的数组

# Any

不希望一个值导致类型检查错误的时候,就可以设置为 any

// any 会把其他变量类型也影响
let a:any 
let b:string 
b = a 
1
2
3
4

# unknown

unknown 表示未知类型的值

let k : unknown
k = "nk"
let j : string
j =k
// 不能将类型“unknown”分配给类型“string”。
1
2
3
4
5

any和unknown的区别就是 unknown不可以赋值给其他变量 实际上unknown就是一个类型安全的any

# 枚举

# 类型注释

当你使用 const、var 或 let 声明一个变量时,你可以选择性的添加一个类型注解,显式指定变量的类型:

let myName: string = "Alice";
1

TypeScript 大多数情况会自动推断类型。

# 函数

函数是 JavaScript 传递数据的主要方法。TypeScript 允许你指定函数的输入值和输出值的类型。

# 参数类型注解(Parameter Type Annotations)

当你声明一个函数的时候,你可以在每个参数后面添加一个类型注解,声明函数可以接受什么类型的参数。参数类型注解跟在参数名字后面:

// Parameter type annotation
function greet(name: string) {
  console.log("Hello, " + name.toUpperCase() + "!!");
}
1
2
3
4

如果入参没有指定类型,会隐式推断为any

# 返回值类型注解(Return Type Annotations)

返回值的类型注解跟在参数列表后面

跟变量类型注解一样,你也不需要总是添加返回值类型注解,TypeScript 会基于它的 return 语句推断函数的返回类型。

function getFavoriteNumber(): number {
  return 26;
}

1
2
3
4

# 匿名函数

当 TypeScript 知道一个匿名函数将被怎样调用的时候,匿名函数的参数会被自动的指定类型。

# 对象类型

// The parameter's type annotation is an object type
function printCoord(pt: { x: number; y: number }) {
  console.log("The coordinate's x value is " + pt.x);
  console.log("The coordinate's y value is " + pt.y);
}
printCoord({ x: 3, y: 7 });

1
2
3
4
5
6
7

对象类型可以指定一些甚至所有的属性为可选的,你只需要在属性名后添加一个 ?

[propName:string]:any 表示任意类型的属性

let obj : {name:string,[propName:string]:any}
obj = {name:'test',ss:'tes'}
1
2

# 联合类型

# 定义一个联合类型(Defining a Union Type)

第一种组合类型的方式是使用联合类型,一个联合类型是由两个或者更多类型组成的类型,表示值可能是这些类型中的任意一个。这其中每个类型都是联合类型的成员(members)。

function printId(id: number | string) {
  console.log("Your ID is: " + id);
}
// OK
printId(101);
// OK
printId("202");
// Error
printId({ myID: 22342 });
1
2
3
4
5
6
7
8
9

TypeScript 会要求你做的事情,必须对每个联合的成员都是有效的

用代码收窄联合类型,就像你在 JavaScript 没有类型注解那样使用。当 TypeScript 可以根据代码的结构推断出一个更加具体的类型时,类型收窄就会出现。 实际上就是对类型进行判断 并进行不同的操作

function welcomePeople(x: string[] | string) {
  if (Array.isArray(x)) {
    // Here: 'x' is 'string[]'
    console.log("Hello, " + x.join(" and "));
  } else {
    // Here: 'x' is 'string'
    console.log("Welcome lone traveler " + x);
  }
}

1
2
3
4
5
6
7
8
9
10

如果联合类型里的每个成员都有一个属性,举个例子,数组和字符串都有 slice 方法,你就可以直接使用这个属性,而不用做类型收窄。

# 类型别名(Type Aliases)

在类型注解里直接使用对象类型和联合类型,这很方便,但有的时候,一个类型会被使用多次,此时我们更希望通过一个单独的名字来引用它。

类型别名,顾名思义,一个可以指代任意类型的名字。

type Point = {x:number; y:number;}
function getPoint(po:Point){
	return `x轴${po.x},y轴${po.y}`
}
getPoint({x:1,y:2})
1
2
3
4
5

# 接口

接口声明(interface declaration)是命名对象类型的另一种方式:

interface Point {
  x: number;
  y: number;
}
 
function printCoord(pt: Point) {
  console.log("The coordinate's x value is " + pt.x);
  console.log("The coordinate's y value is " + pt.y);
}
 
printCoord({ x: 100, y: 100 });

1
2
3
4
5
6
7
8
9
10
11
12

TypeScript 只关心传递给 printCoord 的值的结构(structure)——关心值是否有期望的属性。正是这种只关心类型的结构和能力的特性,我们才认为 TypeScript 是一个结构化(structurally)的类型系统。

# 类型别名和接口的不同

类型别名和接口非常相似,大部分时候,你可以任意选择使用。接口的几乎所有特性都可以在 type 中使用,两者最关键的差别在于类型别名本身无法添加新的属性,而接口是可以扩展的。

interface Animal {
	name:string
}
// 接口通过继承扩展
interface Bear extends Animal {
  honey:Boolean
}
const littleBear: Bear{
  name: 'xiaoxiong',
  honey: true
}

// 别名通过交集拓展类型
type Animal = {
	name:string
}
type Bear=  Animal & {
  honey:true
}
const bigBear:Bear = {
  name:'大熊',
  honey:false
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// 对一个已经定义的接口添加新的字段
interface myWindow {
    title:string
}
interface myWindow {
    hyp: Number
}
const test: myWindow = {
    title: 'ceshi',
    hyp:1
} 
// 别名类型创建以后不能更改
type MyWindow2 = {
    title: string 
}
type MyWindow2 = {
    test: string 
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

# 类型断言

当我们知道某个类型但是ts不知道的时候,我们使用类型断言将其指定为一个更具体的类型

如下

这两种是等价的

const myCanvas = document.getElementById('main_canvas') as HTMLCanvasElement
const myCanvas2 = <HTMLCanvasElement>document.getElementById('main_canvas')
1
2

谨记:因为类型断言会在编译的时候被移除,所以运行时并不会有类型断言的检查,即使类型断言是错误的,也不会有异常或者 null 产生。

TypeScript 仅仅允许类型断言转换为一个更加具体或者更不具体的类型。这个规则可以阻止一些不可能的强制类型转换:

const x = "hello" as number; 
// 会提示错误
// 这时 使用双重断言 
const x = (expr as any) as T; // any或者unknown
1
2
3
4

# 字面量类型(Literal Types)

除了常见的类型 string 和 number ,我们也可以将类型声明为更具体的数字或者字符串。

使用

let x: "hello" = "hello";
// OK
x = "hello";
// ...
x = "howdy";
// Type '"howdy"' is not assignable to type '"hello"'.

1
2
3
4
5
6
7

搭配联合类型

// 指定参数
function printXX(s:string,y:'bixv' | 'cixv' |'aixv') {
    
}
printXX('xx','xx')
//类型“"xx"”的参数不能赋给类型“"bixv" | "cixv" | "aixv"”的参数。

// 指定返回值
function compare(a:number,b:number): -1|1|0 {
    return a===b?0:a>b?1:-1
}
1
2
3
4
5
6
7
8
9
10
11

还有一种字面量类型,布尔字面量。因为只有两种布尔字面量类型, true 和 false ,类型 boolean 实际上就是联合类型 true | false 的别名。

# 字面量推断(Literal Inference)

当你初始化变量为一个对象的时候,TypeScript 会假设这个对象的属性的值未来会被修改

declare function handleRequest(url: string, method: "GET" | "POST"): void;

const req = { url: "https://example.com", method: "GET" };
handleRequest(req.url, req.method);

// Argument of type 'string' is not assignable to parameter of type '"GET" | "POST"'.

1
2
3
4
5
6
7

req.method 被推断为 string ,而不是 "GET",因为在创建 req 和 调用 handleRequest 函数之间,可能还有其他的代码,或许会将 req.method 赋值一个新字符串比如 "Guess"

解决这种情况有两种方式

  1. 添加一个类型断言改变推断结果:
// 1
handleRequest(req.url, req.method as "GET");
// 2
const req = { url: "https://example.com", method: "GET" as "GET" };
1
2
3
4
  1. 也可以使用 as const 把整个对象转为一个类型字面量:
const req = { url: "https://example.com", method: "GET" } as const;
handleRequest(req.url, req.method);
1
2

as const 效果跟 const 类似,但是对类型系统而言,它可以确保所有的属性都被赋予一个字面量类型,而不是一个更通用的类型比如 string 或者 number 。

# null 和undefined

两个原始类型的值,用于表示空缺或者未初始化,他们分别是 null 和 undefined 。

config中的 strictNullChecks 决定是否判断

TypeScript 提供了一个特殊的语法,可以在不做任何检查的情况下,从类型中移除 null 和 undefined 就是在任意表达式后面写上 ! 这是一个有效的类型断言,表示它的值不可能是 null 或者 undefined:

function liveDanger(x?:number | null) {
    // ! 代表断言参数x不可能是 null或者undefined
    console.log(x!.toFixed)
}
1
2
3
4

# 枚举

枚举是 TypeScript 添加的新特性,用于描述一个值可能是多个常量中的一个。不同于大部分的 TypeScript 特性,这并不是一个类型层面的增量,而是会添加到语言和运行时

# 不太常用的原语

# BigInt

ES2020 引入原始类型 BigInt,用于表示非常大的整数:

// Creating a bigint via the BigInt function
const oneHundred: bigint = BigInt(100);
 
// Creating a BigInt via the literal syntax
const anotherHundred: bigint = 100n;

1
2
3
4
5
6

# symbol

const firstName = Symbol("name");
const secondName = Symbol("name");
 
if (firstName === secondName) {
  // This condition will always return 'false' since the types 'typeof firstName' and 'typeof secondName' have no overlap.
  // Can't ever happen
}

1
2
3
4
5
6
7
8

# 类型收窄

# in 操作符收窄

JavaScript 中有一个 in 操作符可以判断一个对象是否有对应的属性名。TypeScript 也可以通过这个收窄类型。

type Fish = {swim:()=>void};
type Bird = {fly:()=>void};
func
1
2
3

# 函数

# 调用签名

函数除了可以被调用,也可以有自己的属性值,这时需要另一种写法来支持属性的描述

type desc = {
    description: string,
    (arg:number):string
}
function descption(fn:desc) {
    return `${fn.description} ${fn(4)}`
}
function toDesc(num:number){
    return `is ${num}`
}
toDesc.description = '这时描述'
console.log( descption(toDesc))
1
2
3
4
5
6
7
8
9
10
11
12

可以看到别名的定义写法是在参数列表和返回类型之间使用: 而不是=>

# 泛型函数

在定义函数或者类 , 遇到类型不明确时, 可以使用泛型

function foo(a:any):any {return}
function fn<T>(a:T):T{return a}
1
2

对比上面的两个函数定义 可以看出 如果使用any 并不能知道返回值和参数的类型是一致的

泛型的使用:

泛型可以同时指定多个

// 直接使用具有泛型的函数
// 直接使用
fn(1)
// 指定类型使用
fn<string>('hhh')
// 定义多个泛型
function foo<T,U>(a:T,b:U):U{
    console.log(a)
    return b
}
// 继承泛型
interface inter{
    length: number
}
// T extends inter 表示 T必须是inter 的实现类(子类)
function boo<T extends inter>(a:T) :number{
    return a.length
}
// 传一个字符串 字符串有length属性
boo<string>('xxx') 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

泛型的作用就是在类型不明确时 定义一个变量 来表示类型

编辑 (opens new window)
上次更新: 2023/02/06, 00:33:25
Typescript面向对象

Typescript面向对象→

最近更新
01
Typescript面向对象
02-05
02
动态规划
12-18
03
pnpm
11-08
更多文章>
Theme by Vdoing | Copyright © 2021-2023 Coderhyp | GITHUB
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式
×