typescript的类型描述_一文学懂TypeScript的类型

你将学到什么

阅读本文后,你应该能够理解以下代码的含义:

interface Array{

concat(...items: Array): T[];

reduce(

callback: (state: U, element: T, index: number, array: T[]) =>U,

firstState?: U): U;

···

}

如果你认为这段代码非常神秘 —— 那么我同意你的意见。但是(我希望证明)这些符号还是相对容易学习的。一旦你能理解它们,就能马上全面、精确的理解这种代码,从而无需再去阅读冗长的英文说明。

运行代码案例

TypeScript 有一个在线运行环境。为了得到最全面的信息,你应该在 “Options” 菜单中打开所有选项开关。这相当于在 --strict 模式下运行TypeScript编译器。

关于类型检查的详细说明

我在用 TypeScript 时总是喜欢打开 --strict 开关设置。没有它,程序可能会稍微好写一点,但是你也失去了静态类型检查的好处。目前此设置能够开启以下子设置:

--noImplicitAny:如果 TypeScript 无法推断类型,则必须指定它。这主要用于函数和方法的参数:使用此设置,你必须对它们进行注释。

--noImplicitThis:如果 this 的类型不清楚则会给出提示信息。

--alwaysStrict:尽可能使用 JavaScript 的严格模式。

--strictNullChecks:null 不属于任何类型(除了它自己的类型,null),如果它是可接受的值,则必须明确指定。

--strictFunctionTypes:对函数类型更加严格的检查。

--strictPropertyInitialization:如果属性的值不能是 undefined ,那么它必须在构造函数中进行初始化。

更多信息:TypeScript 手册中的“编译器选项”一章。

类型

在本文中,我们把类型看作是一组值的集合。 JavaScript 语言(不是TypeScript!)有7种类型:

Undefined:具有***元素 undefined 的集合。

Null:具有***元素“null”的集合。

Boolean:具有两个元素 false 和 true 的集合。

Number:所有数字的集合。

String:所有字符串的集合。

Symbol:所有符号的集合。

Object:所有对象的集合(包括函数和数组)。

所有这些类型都是 dynamic:可以用在运行时。

TypeScript 为 JavaScript 带来了额外的层:静态类型。这些仅在编译或类型检查源代码时存在。每个存储位置(变量或属性)都有一个静态类型,用于预测其动态值。类型检查可确保这些预测能够实现。还有很多可以进行 静态 检查(不运行代码)的东西。例如,如果函数 f(x) 的参数 x 是静态类型 number,则函数调用 f('abc') 是非法的,因为参数 'abc' 是错误的静态类型。

类型注释

变量名后的冒号开始 类型注释:冒号后的类型签名用来描述变量可以接受的值。例如以代码告诉 TypeScript 变量 “x” 只能存储数字:

let x: number;

你可能想知道用 undefined 去初始化 x 是不是违反了静态类型。 TypeScript 不会允许这种情况出现,因为在为它赋值之前不允许操作 x。

类型推断

即使在 TypeScript 中每个存储位置都有静态类型,你也不必总是明确的去指定它。 TypeScript 通常可以对它的类型进行推断。例如如果你写下这行代码:

letx=123;

然后 TypeScript 会推断出 x 的静态类型是 number。

类型描述

在类型注释的冒号后面出现的是所谓的类型表达式。这些范围从简单到复杂,并按如下方式创建。

基本类型是有效的类型表达式:

对应 JavaScript 动态类型的静态类型:

- `undefined`, `null`

- `boolean`, `number`, `string`

- `symbol`

- `object`

注意:值 undefined 与类型 undefined(取决于所在的位置)

TypeScript 的特定类型:

Array(从技术上讲不是 JS 中的类型)

any(所有值的类型)

等等其他类型

请注意,“undefined作为值“ 和 ”undefined作为类型” 都写做 undefined。根据你使用它的位置,被解释为值或类型。 null 也是如此。

你可以通过类型运算符对基本类型进行组合的方式来创建更多的类型表达式,这有点像使用运算符 union(∪)和intersection(∩)去合并集合。

下面介绍 TypeScript 提供的一些类型运算符。

数组类型

数组在 JavaScript 中扮演以下两个角色(有时是两者的混合):

列表:所有元素都具有相同的类型。数组的长度各不相同。

元组:数组的长度是固定的。元素不一定具有相同的类型。

数组作为列表

数组 arr 被用作列表有两种方法表示 ,其元素都是数字:

let arr: number[] = [];

let arr: Array= [];

通常如果存在赋值的话,TypeScript 就可以推断变量的类型。在这种情况下,实际上你必须帮它解决类型问题,因为在使用空数组时,它无法确定元素的类型。

稍后我们将回到尖括号表示法(Array)。

数组作为元组

如果你想在数组中存储二维坐标点,那么就可以把这个数组当作元组去用。看上去是这个样子:

let point: [number, number] = [7, 5];

在这种情况下,你不需要类型注释。

另外一个例子是 Object.entries(obj) 的返回值:一个带有一个 [key,value] 对的数组,它用于描述 obj 的每个属性。

>Object.entries({a:1, b:2})

[ [ 'a', 1 ], [ 'b', 2 ] ]

Object.entries() 的返回值类型是:

Array

函数类型

以下是函数类型的例子:

(num: number) =>string

这个类型是一个函数,它接受一个数字类型参数并且返回值为字符串。在类型注释中使用这种类型(String 在这里是个函数)的例子:

const func: (num: number) =>string=String;

同样,我们一般不会在这里使用类型注释,因为 TypeScript 知道 String 的类型,因此可以推断出 func 的类型。

以下代码是一个更实际的例子:

function stringify123(callback: (num: number) =>string) {

return callback(123);

}

由于我们使用了函数类型来描述 stringify123() 的参数 callback,所以TypeScript 拒绝以下函数调用。

f(Number);

但它接受以下函数调用:

f(String);

函数声明的返回类型

对函数的所有参数进行注释是一个很好的做法。你还可以指定返回值类型(不过 TypeScript 非常擅长去推断它):

function stringify123(callback: (num: number) =>string): string {

const num=123;

return callback(num);

}

特殊返回值类型 void

void 是函数的特殊返回值类型:它告诉 TypeScript 函数总是返回 undefined(显式或隐式):

function f1(): void { return undefined } // OK

function f2(): void { } // OK

function f3(): void { return 'abc' } // error

可选参数

标识符后面的问号表示该参数是可选的。例如:

function stringify123(callback?: (num: number) =>string) {

const num=123;

if (callback) {

return callback(num); // (A)

}

return String(num);

}

在 --strict 模式下运行 TypeScript 时,如果事先检查时发现 callback 没有被省略,它只允许你在 A 行进行函数调用。

参数默认值

TypeScript支持 ES6 参数默认值:

function createPoint(x=0,y=0) {

return [x, y];

}

默认值可以使参数可选。通常可以省略类型注释,因为 TypeScript 可以推断类型。例如它可以推断出 x 和 y 都是 number 类型。

如果要添加类型注释,应该这样写:

function createPoint(x:number=0,y:number=0) {

return [x, y];

}

rest 类型

你还可以用 ES6 rest operator 进行 TypeScript 参数定义。相应参数的类型必须是数组:

function joinNumbers(...nums: number[]): string {

return nums.join('-');

}

joinNumbers(1, 2, 3); // '1-2-3'

Union

在JavaScript中,有时候变量会是有几种类型之中的一种。要描述这些变量,可以使用 union types。例如,在下面的代码中,x 是 null 类型或 number 类型:

letx=null;

x=123;

x 的类型可以描述为 null | number:

let x: null|number=null;

x=123;

类型表达式 s | t 的结果是类型 s 和 t 在集合理论意义上的联合(正如我们之前看到的那样,两个集合)。

下面让我们重写函数 stringify123():这次我们不希望参数 callback 是可选的。应该总是调用它。如果调用者不想传入一个函数,则必须显式传递 null。实现如下。

function stringify123(

callback: null | ((num: number) =>string)) {

const num=123;

if (callback) { // (A)

return callback(123); // (B)

}

return String(num);

}

请注意,在行 B 进行函数调用之前,我们必须再次检查 callback 是否真的是一个函数(行A)。如果没有检查,TypeScript 将会报告错误。

Optional 与 undefined|T

类型为 T 的可选参数和类型为 undefined|T 的参数非常相似。 (另外对于可选属性也是如此。)

主要区别在于你可以省略可选参数:

function f1(x?: number) { }

f1(); // OK

f1(undefined); // OK

f1(123); // OK

But you can’t omit parameters of type

但是你不能省略 undefined|T 类型的参数:

function f2(x: undefined | number) { }

f2(); // error

f2(undefined); // OK

f2(123); // OK

值 null 和 undefined 通常不包含在类型中

在许多编程语言中,null 是所有类型的一部分。例如只要 Java 中的参数类型为 String,就可以传递 null 而Java 不会报错。

相反,在TypeScript中,undefined 和 null 由单独的不相交类型处理。如果你想使它们生效,必须要有一个类型联合,如 undefined|string 和 null|string。

对象

与Arrays类似,对象在 JavaScript 中扮演两个角色(偶尔混合和/或更加动态):

记录:在开发时已知的固定数量的属性。每个属性可以有不同的类型。

字典:在开发时名称未知的任意数量的属性。所有属性键(字符串和/或符号)都具有相同的类型,属性值也是如此。

我们将在本文章中忽略 object-as-dictionaries。顺便说一句,无论如何,map 通常是比字典的更好选择。

通过接口描述 objects-as-records

接口描述 objects-as-records 。例如:

interface Point {

x: number;

y: number;

}

TypeScript 类型系统的一大优势在于它的结构上,而不是在命名上。也就是说,接口 Point 能够匹配适当结构的所有对象:

function pointToString(p: Point) {

return `(${p.x}, ${p.y})`;

}

pointToString({x: 5, y: 7}); // '(5, 7)'

相比之下,Java 的标称类型系统需要类来实现接口。

可选属性

如果可以省略属性,则在其名称后面加上一个问号:

interface Person {

name: string;

company?: string;

}

方法

接口内还可以包含方法:

interface Point {

x: number;

y: number;

distance(other: Point): number;

}

类型变量和泛型类型

使用静态类型,可以有两个级别:

值存在于对象级别。

类型存在于元级别。

同理:

普通变量定义在对象级别之上。

类型变量存在于元级别之上。它们是值为类型的变量。

普通变量通过 const,let 等引入。类型变量通过尖括号( <> )引入。例如以下代码包含类型变量 T,通过 引入。

interface Stack{

push(x: T): void;

pop(): T;

}

你可以看到类型参数 T 在 Stack 的主体内出现两次。因此,该接口可以直观地理解如下:

Stack 是一堆值,它们都具有给定的类型 T。每当你提到 Stack 时,必须写 T。接下来我们会看到究竟该怎么用。

方法 .push() 接受类型为 T 的值。

方法 .pop() 返回类型为 T 的值。

如果使用 Stack,则必须为 T 指定一个类型。以下代码显示了一个虚拟栈,其***目的是匹配接口。

const dummyStack: Stack= {

push(x: number) {},

pop() { return 123 },

};

例子:map

map 在 TypeScript 中的定义。例如:

const myMap: Map= new Map([

[false, 'no'],

[true, 'yes'],

]);

函数的类型变量

函数(和方法)也可以引入类型变量:

function id(x: T): T {

return x;

}

你可以按以下方式使用此功能。

id(123);

由于类型推断,还可以省略类型参数:

id(123);

传递类型参数

函数可以将其她的类型参数传给接口、类等:

function fillArray(len: number, elem: T) {

return new Array(len).fill(elem);

}

类型变量 T 在这段代码中出现三次:

fillArray:引入类型变量

elem:T:使用类型变量,从参数中选择它。

Array:将 T 传递给 Array 的构造函数。

这意味着:我们不必显式指定Array的类型 T —— 它是从参数 elem中推断出来的:

constarr=fillArray(3, '*');

// Inferred type: string[]

总结

让我们用前面学到的知识来理解最开始看到的那段代码:

interface Array{

concat(...items: Array): T[];

reduce(

callback: (state: U, element: T, index: number, array: T[]) =>U,

firstState?: U): U;

···

}

这是一个Array的接口,其元素类型为 T,每当使用这个接口时必须填写它:

方法.concat()有零个或多个参数(通过 rest 运算符定义)。其中每一个参数中都具有类型 T[]|T。也就是说,它是一个 T 类型的数组或是一个 T 值。

方法.reduce() 引入了自己的类型变量 U。 U 表示以下实体都具有相同的类型(你不需要指定,它是自动推断的):

Parameter state of callback() (which is a function)

state 是 callback() 的参数(这是一个函数)

Result of callback()

callback()的返回

reduce()的可选参数 firstState

Result of .reduce()

reduce()的返回

callback 还将获得一个 element 参数,其类型与 Array 元素具有相同的类型 T,参数 index 是一个数字,参数 array 是 T 的值。

【责任编辑:庞桂玉 TEL:(010)68476606】

点赞 0

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

equation在c语言中是什么意思,MathType出现此对象创建于Equation中的问题怎么办

使用MathType出错的窗口&#xff1a;MathType程序停止工作提醒窗口&#xff1a;解决方法如下&#xff1a;1.打开Word文件&#xff0c;在Word菜单中选择“工具”——“模板和加载项”&#xff0c;将会跳出一个模板和加载项的对话框。在Word菜单中选择“工具”——“模板和加载项…

python messagebox弹窗退出_python 弹窗提示警告框MessageBox的实例

需要安装pywin32模块&#xff0c;pip install pywin32 ##pip install pywin32 import win32api,win32con ##提醒OK消息框 win32api.MessageBox(0, "这是一个测试提醒OK消息框", "提醒",win32con.MB_OK) ##是否信息框 win32api.MessageBox(0, "这是一个…

gradle是否可以编译c语言,build.gradle按条件编译与cmake配置

在build.gradle里面通过productFlavors就可以方便的实现不同的编译方案。flavorDimensions定义维度flavorDimensions 从单词字面理解知道是 “风味维度”&#xff0c;是需要结合 “产品风味(即productFlavors)” 来一起使用的。flavorDimensions 的使用会定义出维度&#xff0c…

post请求改成body_post请求body格式

在PostMan中用Post方式&#xff0c;Body有form-data,x-www-form-urlencoded,raw,binary四种。其中raw又分以下7种。现在来区分一下&#xff1a;form-data是http请求中的multipart/form-data,它会将表单的数据处理为一条消息&#xff0c;以标签为单元&#xff0c;用分隔符分开。…

windows多用户 文件夹不共享_手把手教你如何使用Tekla多用户

Tekla有多用户模式&#xff0c;对于大模型需要多人合作很有用&#xff0c;可以多人同时建模互不干扰&#xff0c;下面简单说下多用户建立过程。 首先需要参与模型的计算机处于同一局域网内&#xff0c;一般来说公司都有局域网&#xff0c;或者办公室内就是一个小局域网&#xf…

roads 用户体验标准_世界智能大会与ROAD用户体验报告

近期由国家发展和改革委员会、科学技术部、工业和信息化部、国家互联网信息办公室等共同举办的2020年第四届世界智能大会在天津云上展开&#xff0c;超过百余位智能科技领域的知名专家和企业家参与了大会过程&#xff0c;其中车联网领域专家关于5G车联网”推进中国特色的车路协…

android 组件路由框架,XRouter:组件化路由框架

添加jitpack仓库allprojects {repositories {...maven { url https://jitpack.io }}}添加依赖&#xff1a;dependencies {//kotlin 使用kapt编译时依赖注解&#xff0c;Java使用annotationProcessorkapt com.github.roger1245.XRouter:xrouter-compiler:1.0.2api com.github.ro…

hystrix原理_面试必问的SpringCloud实现原理图

引言面试中面试官喜欢问组件的实现原理&#xff0c;尤其是常用技术&#xff0c;我们平时使用了SpringCloud还需要了解它的实现原理&#xff0c;这样不仅起到举一反三的作用&#xff0c;还能帮助轻松应对各种问题及有针对的进行扩展。以下是《Java深入微服务原理改造房产销售平台…

android 图片跑马灯动画,ImageView 图片循环跑马灯的效果

不解释了 直接上代码了main.xml布局文件&#xff0c;记住必须用RelativeLayout将ImageView重叠android:orientation"vertical" android:layout_width"fill_parent"android:layout_height"fill_parent" android:id"id/rl">android:…

Rust Trait

Rust 第16节 Trait Trait 告诉编译器 某种类型具有那些并且可以与其他类型共享的功能 它的本质就是 不同类型具有的相同行为 声明一个trait 关键字 trait;只有方法签名&#xff0c;没有方法实现 pub trait Animal {// trait 的声明,一个trait中可以有多个方法fn say(&s…

c++ string类的常用方法_【常用类方法】Object

Object类的知识点总结概述&#xff1a;1. Object类是所有其他类的父类2. Object类只有一个构造方法&#xff0c;这也是为什么所有子类在调用构造方法时都会默认先调用父类的无参构造方法3. Object类没有成员变量方法&#xff1a;1. public int hashCode()2. public final Class…

android 收获地址管理,android UiAutomator添加收货地址的用例

本人在学习UiAutomator的时候&#xff0c;遇到添加收获地址的测试用例&#xff0c;这里的地址的地区是一级一级选择的。所以写了一个随机选择的方法。分享出来&#xff0c;供大家参考。public void addAdress() throws UiObjectNotFoundException {login();waitForResourceIdAn…

python注释以符号什么开始_python注释以什么符号开始

python注释以什么符号开始,注释,中文,代码,批量,符号 python注释以什么符号开始 易采站长站&#xff0c;站长之家为您整理了python注释以什么符号开始的相关内容。 python中的注释有多种&#xff0c;有单行注释&#xff0c;多行注释&#xff0c;批量注释&#xff0c;中文注释也…

verilog 移位运算符 说明_Verilog学习笔记基本语法篇(二)·········运算符...

Verilog HDL的语言的运算符的范围很广&#xff0c;按照其功能大概可以分为以下几类:(1)算术运算符&#xff0c;-&#xff0c;*&#xff0c;/&#xff0c;%优先顺序&#xff01;~* / % -<< >>< < > > ! !&^ ^~|&&||&…

linux 别名,Linux中的别名就这么简单,如何使用和创建永久别名?

原标题&#xff1a;Linux中的别名就这么简单&#xff0c;如何使用和创建永久别名&#xff1f;输入文本和记命令是Linux命令行爱好者的缺点之一。如果你需要输入并记住同样长的命令&#xff0c;这可能会降低终端的工作效率。如果您可以用自己的短名称替换长命令&#xff0c;或者…

华为手机如何固定横屏_华为手机如何录屏?原来方法这么简单,手把手教你学会...

很多人都不知道&#xff0c;华为手机到底如何录屏&#xff0c;下面给大家分享4种方法&#xff0c;非常简单&#xff0c;手把手教你学会。一、通知栏录屏从手机顶端往下滑动打开通知栏&#xff0c;这个面板上有很多快捷功能&#xff0c;其中就有【屏幕录制】功能&#xff0c;点击…

程序发出的广播其他程序收不到_RabbitMQ 如何实现对同一个应用的多个节点进行广播...

1.背景了解过RabbitMQ的Fanout模式&#xff0c;应该知道它原本的Fanout模式就是用来做广播的。但是它的广播有一点区别&#xff0c;来回顾下它的含义&#xff1a;Fanout类型没有路由键的概念&#xff0c;只要队列绑定到了改exchange上面&#xff0c;就会接收到所有的消息。使用…

android listview高级,Android 高级控件笔记-列表视图ListView 基本适配器BaseAdapter

我最近做项目用到了基本适配器BaseAdapter&#xff0c;所以写篇博客总结一下&#xff0c;希望也能对你有所帮助什么时候用BaseAdapter(同一项存在多个控件&#xff0c;复杂的列表时)Android中Adapter类其实就是把数据源绑定到指定的View上&#xff0c;然后再返回该View&#xf…

cnn生成图像显著图_基于CNN与图像前背景分离的显著目标检测

基于CNN与图像前背景分离的显著目标检测东野长磊;万文鑫【期刊名称】《软件导刊》【年(卷),期】2020(019)001【摘要】为了解决计算机视觉模拟人眼的视觉机制,显著性目标检测DSS(DeeplySupervisedSalient)在某个场景中人眼首先观察到的目标.基于卷积神经网络和图像前背景分离算法…

api 创建zookeeper客户端_一文了解 Zookeeper 基本原理与应用场景

Zookeeper 是一个高性能、高可靠的分布式协调系统&#xff0c;是 Google Chubby 的一个开源实现&#xff0c;目前在分布式系统、大数据领域中使用非常广泛。本文将介绍 Zookeeper 集群架构、数据模型、监听机制&#xff0c;以及Zookeeper典型的应用场景等。1. Zookeeper 集群角…