Typescript学习笔记(一)

什么是TypeScript?

TypeScript 是添加了类型系统的 JavaScript,适用于任何规模的项目。
TypeScript 是一门静态类型、弱类型的语言。

安装TypeScript

npm install -g typescript

编译

tsc hello.ts

TypeScript 只会在编译时对类型进行静态检查,如果发现有错误,编译的时候就会报错。而在运行时,与普通的 JavaScript 文件一样,不会对类型进行检查。

TypeScript 编译的时候即使报错了,还是会生成编译结果。

基础

1、原始数据类型

JavaScript基本类型TypeScript基本类型备注
布尔值boolean注意Boolean对象不是布尔值
数值numberES6中的二进制和八进制会被编译为十进制数字
字符串string
空值voidJs中没有空值的概念,在 TypeScript 中,可以用 void 表示没有任何返回值的函数
Nullnull
Undefinedundefinedundefined 和 null 是所有类型的子类型。也就是说 undefined 类型的变量,可以赋值给 number 类型的变量,而void 类型的变量不能赋值给 number 类型的变量

2、任意值

  • 任意值(Any)用来表示允许赋值为任意类型。,普通类型在赋值过程中改变类型是不被允许的;
  • 在任意值上访问任何属性都是允许的;
  • 在任意值上调用任何方法都是允许的;
  • 声明一个变量为任意值之后,对它的任何操作,返回的内容的类型都是任意值。
  • 变量如果在声明的时候,未指定其类型,那么它会被识别为任意值类型

3、类型推论

如果没有明确的指定类型,那么 TypeScript 会依照类型推论(Type Inference)的规则推断出一个类型。
举个例子:

let a = '123'
a = 1

编译

tsc hello.ts 
hello.ts:2:1 - error TS2322: Type 'number' is not assignable to type 'string'.

2 a = 1
  ~


Found 1 error in hello.ts:2

如果定义的时候没有赋值,不管之后有没有赋值,都会被推断成 any 类型而完全不被类型检查。

4、联合类型

联合类型(Union Types)表示取值可以为多种类型中的一种。
联合类型使用 | 分隔每个类型。

let a: string | number
a = 'app'
a = 1

当一个变量被声明(但未赋值时)为联合类型,仅能访问联合类型中所有类型都共有的属性或方法。
联合类型的变量在被赋值的时候,会根据类型推论的规则推断出一个类,此时可以正常访问该类型的属性或方法。

5、对象的类型——接口

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

// 定义接口
interface Animal {
	type: string,
	age: number
}

// 使用该接口声明变量
let cat : Animal = {
	name: 'mimi',
	age: 2
}

可选属性

在使用接口声明变量时,多声明属性或少声明属性都不被允许。
但我们可以使用可选属性来达到不完全匹配接口属性的目的,可选属性的含义是该属性可以不存在,但仍然不允许添加未定义的属性

// 定义接口
interface Animal {
	type: string,
	age?: number
}

任意属性

在接口中定义任意属性,即可让我们能够在声明变量是添加一个任意的属性。

// 定义接口
interface Animal {
	type: string,
	[propName: string]: any;
}

// 声明变量
// 使用该接口声明变量
let cat : Animal = {
	name: 'mimi',
	gender: 'female'
}

注意:一旦定义了任意属性,那么确定属性和可选属性的类型都必须是它的类型的子集:

interface Animal {
	type: string,
	age: number
	[propName: string]: string;
}

上面这段代码会报错,因为任意属性只允许string,但age属性声明为了number

只读属性

使用readonly定义只读属性,该属性仅在创建的时候可被赋值
注意,只读的约束存在于第一次给对象赋值的时候,而不是第一次给只读属性赋值的时候

6、数组的类型

在TypeScript中可以使用多种方式定义数组:

  • 类型+方括号:let arr: number[] = [1,2,3,4,5]
  • 数组泛型:let arr: Array<number> = [1,2,3,4,5]
  • 用接口便是数组:
interface NumberArray {
	[index: number]: number
}
let arr:NumberArray = [1,2,3,4,5]
// NumberArray 表示:只要索引的类型是数字时,那么值的类型必须是数字。
  • 类数组:
    类数组不是数组类型,比如函数的arguments参数
function sum() {
    let args: number[] = arguments;
}

// Type 'IArguments' is missing the following properties from type 'number[]': pop, push, concat, join, and 24 more.

上例中,arguments 实际上是一个类数组(常用的类数组有:IArgumentsNodeListHTMLCollection),不能用普通的数组的方式来描述,而应该用接口:

function sum() {
    let args: {
        [index: number]: number;
        length: number;
        callee: Function;
    } = arguments;
}

另外,可以使用any表示数组中允许出现任意类型:

let arr: any[] = ['a', 1, true]

7、函数的类型

js中有两种常见的函数定义方式:

  • 函数声明
  • 函数表达式

函数声明

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

function sum(x:number, y:number): number {
	return x + y
}

函数表达式

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

在这里插入图片描述
注意不要混淆了 TypeScript 中的 => 和 ES6 中的 =>。

在 TypeScript 的类型定义中,=> 用来表示函数的定义,左边是输入类型,需要用括号括起来,右边是输出类型。

用接口定义函数的形状

interface SearchFunc {
	(source: stirng, subString: string): boolean
}

let mySearch: SearchFunc;
mySearch = function(source: string, subString: string) {
	return source.search(subString) !== -1
}

可选参数

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

function buildName(firstName: string, lastName?: string) {
    if (lastName) {
        return firstName + ' ' + lastName;
    } else {
        return firstName;
    }
}
let tomcat = buildName('Tom', 'Cat');
let tom = buildName('Tom');

参数默认值

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

function buildName(firstName: string = 'Tom', lastName: string) {
    return firstName + ' ' + lastName;
}
let tomcat = buildName('Tom', 'Cat'); // Tom Cat
let cat = buildName(undefined, 'Cat'); // Tom Cat

剩余参数

重载

// 函数定义
function reverse(x: number): number;
// 函数定义
function reverse(x: string): string;
// 函数实现
function reverse(x: number | string): number | string | void {
    if (typeof x === 'number') {
        return Number(x.toString().split('').reverse().join(''));
    } else if (typeof x === 'string') {
        return x.split('').reverse().join('');
    }
}

注意,TypeScript 会优先从最前面的函数定义开始匹配,所以多个函数定义如果有包含关系,需要优先把精确的定义写在前面。

8、类型断言

语法

as 类型
// 例如 mimi as Cat

用途

将一个联合类型断言为其中其中一个类型

我们知道,我们只能访问联合类型中所有类型公有的属性和方法,否则在编译时会报错,为了“欺骗”TypeScript编译器,我们可以使用类型断言

interface Cat {
    name: string;
    run(): void;
}
interface Fish {
    name: string;
    swim(): void;
}

function isFish(animal: Cat | Fish) {
    if (typeof (animal as Fish).swim === 'function') {
        return true;
    }
    return false;
}

但这种方法只能避免编译错误,无法避免运行时错误
使用类型断言时一定要格外小心,尽量避免断言后调用方法或引用深层属性,以减少不必要的运行时错误。

将一个父类断言为更具体的子类

class ApiError extends Error {
    code: number = 0;
}
class HttpError extends Error {
    statusCode: number = 200;
}

function isApiError(error: Error) {
    if (typeof (error as ApiError).code === 'number') {
        return true;
    }
    return false;
}

将任何一个类型断言为any

window.foo = 1;

// index.ts:1:8 - error TS2339: Property 'foo' does not exist on type 'Window & typeof globalThis'.

此时可以使用断言,将window断言为any类型

(window as any).foo = 1

将一个变量断言为 any 可以说是解决 TypeScript 中类型问题的最后一个手段。它极有可能掩盖了真正的类型错误,所以如果不是非常确定,就不要使用 as any。

将any断言为一个具体的类型

function getCacheData(key: string): any {
    return (window as any).cache[key];
}

interface Cat {
    name: string;
    run(): void;
}

const tom = getCacheData('tom') as Cat;
tom.run();

上面的例子中,我们调用完 getCacheData 之后,立即将它断言为 Cat 类型。这样的话明确了 tom 的类型,后续对 tom 的访问时就有了代码补全,提高了代码的可维护性。

类型断言的限制

若 A 兼容 B,那么 A 能够被断言为 B,B 也能被断言为 A

9、声明文件

declare var 声明全局变量
declare function 声明全局方法
declare class 声明全局类
declare enum 声明全局枚举类型
declare namespace 声明(含有子属性的)全局对象
interfacetype 声明全局类型
export 导出变量
export namespace 导出(含有子属性的)对象
export default ES6 默认导出
export = commonjs 导出模块
declare global 扩展全局变量
declare module 扩展模块
/// <reference /> 三斜线指令

声明语句

declare var jQuery: (selector: string) => any;

jQuery('#foo');

声明文件

声明文件即:将声明语句放在一个单独的文件(jQuery.d.ts)中
声明文件必需以 .d.ts 为后缀。
如何书写声明文件?

declare var /declare let/ declare const

declare const name = 'abc'

// 声明语句中只能定义类型,切勿在声明语句中定义具体的实现,以下代码会报错
declare const getAge = function(name: string) {
	return 18
}

declare function

declare function jQuery(selector: string): any;

declare class

也只能定义类型,不能写具体实现

declare class {
	name: string,
	age: number,
	sayHi(): string 
}

declare enum定义外部枚举

声明文件:

// src/Directions.d.ts

declare enum Directions {
    Up,
    Down,
    Left,
    Right
}

使用:

// src/index.ts

let directions = [Directions.Up, Directions.Down, Directions.Left, Directions.Right];

其中 Directions 是由第三方库定义好的全局变量。

npm包

如何看npm包是否存在声明文件?
npm包的声明文件可能存在于2个地方:

  • 与该 npm 包绑定在一起。判断依据是 package.json 中有 types 字段,或者有一个 index.d.ts 声明文件。这种模式不需要额外安装其他包,是最为推荐的,所以以后我们自己创建 npm 包的时候,最好也将声明文件与 npm 包绑定在一起。
  • 发布到 @types 里。我们只需要尝试安装一下对应的 @types 包就知道是否存在该声明文件,安装命令是 npm install @types/foo --save-dev。这种模式一般是由于 npm 包的维护者没有提供声明文件,所以只能由其他人将声明文件发布到 @types 里了。

如果通过以上两种方式没有找到对应的声明文件,则需要自己编写:
创建一个 types 目录,专门用来管理自己写的声明文件,将 foo 的声明文件放到 types/foo/index.d.ts 中。这种方式需要配置下 tsconfig.json 中的 paths 和 baseUrl 字段。
如何编写可参考:http://ts.xcatliu.com/basics/declaration-files.html

模块插件 declare module

有时通过 import 导入一个模块插件,可以改变另一个原有模块的结构。此时如果原有模块已经有了类型声明文件,而插件模块没有类型声明文件,就会导致类型不完整,缺少插件部分的类型。ts 提供了一个语法 declare module,它可以用来扩展原有模块的类型。
扩展原有模块:

// types/moment-plugins/index.d.ts

import * as moment from 'moment' // 引入原模块

declare module 'moment' {
	export function foo(): moment.Calendarkey
}
// src/index.ts

import * as moment from 'moment';
import 'moment-plugin';

moment.foo();

声明文件中的依赖

一个声明文件有时会依赖另一个声明文件中的类型,比如在前面的 declare module 的例子中,我们就在声明文件中导入了 moment,并且使用了 moment.CalendarKey 这个类型:

// types/moment-plugin/index.d.ts

import * as moment from 'moment';

declare module 'moment' {
    export function foo(): moment.CalendarKey;
}

除了可以在声明文件中通过 import 导入另一个声明文件中的类型之外,还有一个语法也可以用来导入另一个声明文件,那就是三斜线指令

三斜线指令用于声明模块之间的依赖关系
类似于声明文件中的 import,它可以用来导入另一个声明文件。与 import 的区别是,当且仅当在以下几个场景下,我们才需要使用三斜线指令替代 import:

  • 当我们在书写一个全局变量的声明文件时
  • 当我们需要依赖一个全局变量的声明文件时

1、书写一个全局变量的声明文件
在全局变量的声明文件中,是不允许出现 import, export 关键字的。一旦出现了,那么他就会被视为一个 npm 包或 UMD 库,就不再是全局变量的声明文件了。故当我们在书写一个全局变量的声明文件时,如果需要引用另一个库的类型,那么就必须用三斜线指令了
三斜线指令必须放在文件的最顶端,三斜线指令的前面只允许出现单行或多行注释

// env.d.ts

/// <reference types="vite/client" />

2、依赖一个全局变量的声明文件
在另一个场景下,当我们需要依赖一个全局变量的声明文件时,由于全局变量不支持通过 import 导入,当然也就必须使用三斜线指令来引入了

// types/node-plugin/index.d.ts

/// <reference types="node" />

export function foo(p: NodeJS.Process): string;
// src/index.ts

import { foo } from 'node-plugin';

foo(global.process);

在上面的例子中,我们通过三斜线指引入了 node 的类型,然后在声明文件中使用了 NodeJS.Process 这个类型。最后在使用到 foo 的时候,传入了 node 中的全局变量 process。
由于引入的 node 中的类型都是全局变量的类型,它们是没有办法通过 import 来导入的,所以这种场景下也只能通过三斜线指令来引入了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/7858.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

iOS 内存管理机制与原理

内存分区 内存一般分为五大区&#xff1a;栈区、堆区、常量区、全局区、代码区。如图 1.栈区 是由编译器自动分配并释放的&#xff0c;主要用来存储局部变量、函数的参数等&#xff0c;是一块连续的内存区域&#xff0c;遵循先进后出&#xff08;FILO&#xff09;原则。一般在…

WebAssembly 助力云原生:APISIX 如何借助 Wasm 插件实现扩展功能?

本文将介绍 Wasm&#xff0c;以及 Apache APISIX 如何实现 Wasm 功能。 作者朱欣欣&#xff0c;API7.ai 技术工程师 原文链接 什么是 Wasm Wasm 是 WebAssembly 的缩写。WebAssembly/Wasm 是一个基于堆栈的虚拟机设计的指令格式。 在 Wasm 未出现之前&#xff0c;浏览器中只能…

Hadoop(伪分布式)+Spark(local模式)搭建Hadoop和Spark组合环境

一、安装Hadoop环境使用Ubuntu 14.04 64位 作为系统环境&#xff08;Ubuntu 12.04&#xff0c;Ubuntu16.04 也行&#xff0c;32位、64位均可&#xff09;&#xff0c;请自行安装系统。Hadoop版本: Hadoop 2.7.4创建hadoop用户如果你安装 Ubuntu 的时候不是用的 "hadoop&qu…

研究的艺术 (The craft of research) 读书笔记

前言 如果你对这篇文章感兴趣&#xff0c;可以点击「【访客必读 - 指引页】一文囊括主页内所有高质量博客」&#xff0c;查看完整博客分类与对应链接。 对于研究者而言&#xff0c;写作是一件很重要的事&#xff0c;好的写作不仅能让更多人愿意读&#xff0c;获得更大影响力&…

Windows系统配置SSH服务

1.安装OpenSSH 打开【设置】-【应用】 选择【管理可选功能】 点击【添加可选功能】 选择【OpenSSH 服务端】&#xff0c;切记不是【OpenSSH 客户端】&#xff08;如果安装一个不行&#xff0c;就都安装&#xff0c;我都安装了可以用&#xff09;&#xff0c;然后点击下载即可 …

ERP系统如何让项目管理更轻松?

项目管理是许多企业的首要任务&#xff0c;通常有一个ERP系统来协助他们。然而&#xff0c;一些企业仍然没有意识到&#xff0c;ERP解决方案可以使他们的项目管理更容易。项目管理需要有一个目标&#xff0c;并在你朝着完成项目前进的过程中控制变量&#xff0c;而ERP系统指导你…

成都北大青鸟怎么样?

对于任何一个培训机构的了解大概的流程是&#xff1a;听说名字——网上搜索&#xff0c;可以看到机构官网&#xff0c;也会看到机构广告&#xff0c;当然也会看到各种有好有坏的评论&#xff0c;到这里会对机构形成初印象&#xff1b;然后如果身边有培训出身的小伙伴会去询问对…

【C语言进阶:自定义类型详解】联合(共用体)

本节重点内容&#xff1a; 联合类型的定义联合的特点联合大小的计算⚡联合类型的定义 联合也是一种特殊的自定义类型这种类型定义的变量也包含一系列的成员&#xff0c;特征是这些成员公用同一块空间&#xff08;所以联合也叫共用体&#xff09;。 为了方便大家理解举个例子…

Java 网络编程之NIO(ByteBuffer)

在 Java NIO 中&#xff0c;ByteBuffer 是用于存储和传输数据的一种数据结构。它提供了高效的数据存储和读取能力&#xff0c;使得 Java NIO 能够高效地处理大量的数据输入输出。 ByteBuffer 的作用包括以下几个方面: 存储数据:ByteBuffer 可以存储任意长度的数据&#xff0c;…

Elasticsearch 8.X 如何基于用户指定 ID 顺序召回数据?

1、实战问题如何根据输入的id 的顺序输出结果&#xff0c;id 个数有500个&#xff0c;还有分页&#xff1f;问题来源&#xff1a;https://t.zsxq.com/0cdyq7tzr2、方案探讨2.1 Elasticsearch 默认排序机制在 Elasticsearch 中&#xff0c;如果未指定排序规则&#xff0c;检索结…

Linux下实现的 HTTP 服务器

项目功能&#xff1a;&#xff08;1&#xff09;能接收客户端的GET请求&#xff1b;&#xff08;2&#xff09;能够解析客户端的请求报文&#xff0c;根据客户端要求找到相应的资源&#xff1b;&#xff08;2&#xff09;能够回复http应答报文&#xff1b;&#xff08;3&#x…

数据结构和算法学习记录——设计循环队列(数组实现循环队列)核心思路、题解过程、完整题解

目录 题目描述 题目示例 核心思路 链表实现 数组实现 重点 题解过程 结构体类型定义 创建一个循环队列并初始化 判断循环队列为空或为满 入队列函数 出队列函数 取队头数据 取队尾数据 销毁循环队列 完整题解 题目来源&#xff1a;力扣 题目描述 设计你的…

Sentinel滑动时间窗限流算法原理及源码解析(下)

文章目录对统计数据如何使用获取之前统计好的数据对统计数据如何使用 流控快速失败 获取之前统计好的数据

SpringBoot 项目的创建与启动

✅作者简介&#xff1a;2022年博客新星 第八。热爱国学的Java后端开发者&#xff0c;修心和技术同步精进。 &#x1f34e;个人主页&#xff1a;Java Fans的博客 &#x1f34a;个人信条&#xff1a;不迁怒&#xff0c;不贰过。小知识&#xff0c;大智慧。 &#x1f49e;当前专栏…

iosApplePay

1、Apple Pay 接入工程 - 简书 // 设置币种、国家码及merchant标识符等基本信息 PKPaymentRequest *payRequest [[PKPaymentRequest alloc]init]; payRequest.countryCode "CN"; //国家代码 payRequest.currencyCode "CNY"; //RMB的币种代码 …

“被裁员之前,没得到任何风声,措手不及...” 一个在职6年测试工程师内心独白

前言 一个码农&#xff08;软件测试工程师&#xff09;的自白 小张&#xff1a; 我们用工作五年的积蓄&#xff0c;在这个一线城市买了房子&#xff0c;买了车子&#xff0c;然后领证。我也在6年前进入了一个很多人梦寐以求新的公司 码农的新生活开始了。在这家公司里&…

ChatGPT如何为企业提供帮助?

数字化转型是指利用技术来改变企业的运营方式并为客户提供价值&#xff0c;这不仅仅是关于如何采用新的技术或工具。要想取得成功&#xff0c;就需要从根本上改变文化和心态。 ChatGPT如何为企业提供帮助?从数据分析到知识管理再到客户服务等等&#xff0c;人工智能聊天机器人…

光伏发电系统模拟及其发电预测开源python工具pvlib

1. 太阳辐照量模拟 pysolar是一个用于计算太阳位置和辐照量的Python库。它是基于python语言编写的&#xff0c;可以方便地在各种python项目中使用。pysolar主要用于计算太阳的位置、太阳高度角、太阳方位角、日出和日落时间等信息。这些信息可以用于太阳能电池板和太阳能集热器…

【设计模式】创建型-抽象工厂模式

文章目录一、抽象工厂模式1.1、产品族、产品等级1.2、抽象工厂模式中的角色1.3、实例一、抽象工厂模式 在工厂方法模式中&#xff0c;每一个具体的工厂子类只能生成一种具体的产品&#xff0c;如果想要生产另外一种产品&#xff0c;就需要重新定义一个抽象工厂类&#xff0c;这…

泡泡玛特“失速”,盲盒经济迎来拐点?

配图来自Canva可画​ 前些年泡泡玛特的飞速增长&#xff0c;曾经在行业内外引起了广泛的反响&#xff0c;其主打的盲盒经济也曾风靡一时、被众多行业效仿。不过&#xff0c;这种情况在疫情肆虐的2022年似乎受到了一些影响&#xff0c;这在其财报中就有所体现。 3月29日&#…