1、TypeScript 是静态类型,是弱类型
全局安装ts: npm install -g typescript
编译一个 TypeScript 文件:tsc hello.ts
TypeScript 最大的优势之一便是增强了编辑器和 IDE 的功能,包括代码补全、接口提示、跳转到定义、重构
TypeScript 中,使用 :
指定变量的类型。TypeScript 只会在编译时对类型进行静态检查,如果发现有错误,编译的时候就会报错。tsc hello.ts --noEmitOnError
原始数据类型包括:布尔值、数值、字符串、null
、undefined
以及 ES6 中的新类型 Symbol
和 ES10 中的新类型 BigInt
bool: let isDone: boolean = false;
使用构造函数 Boolean
创造的对象不是布尔值:
let createdByNewBoolean: boolean = new Boolean(1);
直接调用Boolean
也可以返回一个boolean
类型
let createdByBoolean: boolean = Boolean(1);
数值:
let decLiteral: number = 6;字符串
let myName: string = 'Tom';
// 模板字符串 let sentence: string = `Hello, my name is ${myName}. I'll be ${myAge + 1} years old next month.`;
JavaScript 没有空值(Void)的概念,在 TypeScript 中,可以用 void
表示没有任何返回值的函数:
function alertName(): void { alert('My name is Tom'); }
TypeScript 中,可以使用 null
和 undefined
来定义这两个原始数据类型:
let u: undefined = undefined;
let n: null = null;undefined
和null
是所有类型的子类型。
任意值(Any)用来表示允许赋值为任意类型。
let myFavoriteNumber: any = 'seven'; myFavoriteNumber = 7;
,声明一个变量为任意值之后,对它的任何操作,返回的内容的类型都是任意值。
变量如果在声明的时候,未指定其类型,那么它会被识别为任意值类型
let something; something = 'seven'; something = 7;如果没有明确的指定类型,那么 TypeScript 会依照类型推论(Type Inference)的规则推断出一个类型。 如果定义的时候没有赋值,不管之后有没有赋值,都会被推断成
any
类型而完全不被类型检查:
联合类型(Union Types)表示取值可以为多种类型中的一种。
let myFavoriteNumber: string | number; myFavoriteNumber = 'seven'; myFavoriteNumber = 7;
当 TypeScript 不确定一个联合类型的变量到底是哪个类型的时候,我们只能访问此联合类型的所有类型里共有的属性或方法
在 TypeScript 中,我们使用接口(Interfaces)来定义对象的类型。
在面向对象语言中,接口(Interfaces)是一个很重要的概念,它是对行为的抽象,而具体如何行动需要由类(classes)去实现(implement)。
TypeScript 中的接口是一个非常灵活的概念,除了可用于对类的一部分行为进行抽象以外,也常用于对「对象的形状(Shape)」进行描述。
interface Person { name: string; age: number; } let tom: Person = { name: 'Tom', age: 25 };
上面的例子中,我们定义了一个接口 Person
,接着定义了一个变量 tom
,它的类型是 Person
。这样,我们就约束了 tom
的形状必须和接口 Person
一致。
接口一般首字母大写。有的编程语言中会建议接口的名称加上 I
前缀。赋值的时候,变量的形状必须和接口的形状保持一致。
有时我们希望不要完全匹配一个形状,那么可以用可选属性
interface Person { name: string; age?: number; } let tom: Person = { name: 'Tom' };
这时仍然不允许添加未定义的属性
有时候我们希望一个接口允许有任意的属性,可以使用如下方式:
interface Person { name: string; age?: number; [propName: string]: any; } let tom: Person = { name: 'Tom', gender: 'male' };
需要注意的是,一旦定义了任意属性,那么确定属性和可选属性的类型都必须是它的类型的子集:
interface Person { name: string; age?: number; [propName: string]: string | number; } let tom: Person = { name: 'Tom', age: 25, gender: 'male' };
有时候我们希望对象中的一些字段只能在创建的时候被赋值,那么可以用readonly
定义只读属性:
interface Person { readonly id: number; name: string; age?: number; [propName: string]: any; } let tom: Person = { id: 89757, name: 'Tom', gender: 'male' };
注意,只读的约束存在于第一次给对象赋值的时候,而不是第一次给只读属性赋值的时候
在 TypeScript 中,数组类型有多种定义方式
用「类型 + 方括号」来表示数组,组的项中不允许出现其他的类型
let fibonacci: number[] = [1, 1, 2, 3, 5];
使用数组泛型(Array Generic)Array<elemType>
来表示数组
let fibonacci: Array<number> = [1, 1, 2, 3, 5];
接口也可以用来描述数组
interface NumberArray { [index: number]: number; } let fibonacci: NumberArray = [1, 1, 2, 3, 5];
类数组(Array-like Object)不是数组类型,比如arguments
function sum() { let args: number[] = arguments; }
arguments
实际上是一个类数组,不能用普通的数组的方式来描述,而应该用接口:
function sum() { let args: { [index: number]: number; length: number; callee: Function; } = arguments; }
any
表示数组中允许出现任意类型
let list: any[] = ['xcatliu', 25, { website: 'http://xcatliu.com' }];
javaScript 中,有两种常见的定义函数的方式——函数声明(Function Declaration)和函数表达式(Function Expression):
TypeScript 中对其进行约束,需要把输入和输出都考虑到,输入多余的(或者少于要求的)参数,是不被允许的:
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 中,=>
叫做箭头函数,应用十分广泛,可以参考 ES6 中的箭头函数。
可选的参数,可选参数必须接在必需参数后面。换句话说,可选参数后面不允许再出现必需参数了:
function buildName(firstName: string, lastName?: string) { if (lastName) { return firstName + ' ' + lastName; } else { return firstName; } } let tomcat = buildName('Tom', 'Cat'); let tom = buildName('Tom');
TypeScript 会将添加了默认值的参数识别为可选参数,此时就不受「可选参数必须接在必需参数后面」的限制了:
使用...rest
的方式获取函数中的剩余参数(rest 参数)
function push(array: any[], ...items: any[]) { items.forEach(function(item) { array.push(item); }); } let a = []; push(a, 1, 2, 3);
重载允许一个函数接受不同数量或类型的参数时,作出不同的处理。
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 会优先从最前面的函数定义开始匹配,所以多个函数定义如果有包含关系,需要优先把精确的定义写在前面。
类型断言(Type Assertion)可以用来手动指定一个值的类型。
值 as 类型
或
<类型>值
建议大家在使用类型断言时,统一使用 值 as 类型
这样的语法,本书中也会贯彻这一思想
将一个联合类型断言为其中一个类型
需要注意的是,类型断言只能够「欺骗」TypeScript 编译器,无法避免运行时的错误,反而滥用类型断言可能会导致运行时错误:
类之间有继承关系时,类型断言
interface ApiError extends Error
它极有可能掩盖了真正的类型错误,所以如果不是非常确定,就不要使用as any
。一方面不能滥用as 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();
- 联合类型可以被断言为其中一个类型
- 父类可以被断言为子类
- 任何类型都可以被断言为 any
- any 可以被断言为任何类型
类型断言 vs 泛型
function getCacheData<T>(key: string): T { return (window as any).cache[key]; } interface Cat { name: string; run(): void; } const tom = getCacheData<Cat>('tom'); tom.run();
通过给getCacheData
函数添加了一个泛型<T>
,我们可以更加规范的实现对getCacheData
返回值的约束,这也同时去除掉了代码中的any
,是最优的一个解决方案。
declare var
声明全局变量declare function
声明全局方法declare class
声明全局类declare enum
声明全局枚举类型declare namespace
声明(含有子属性的)全局对象interface
和type
声明全局类型export
导出变量export namespace
导出(含有子属性的)对象export default
ES6 默认导出export =
commonjs 导出模块export as namespace
UMD 库声明全局变量declare global
扩展全局变量declare module
扩展模块/// <reference />
三斜线指令
declare var jQuery: (selector: string) => any; jQuery('#foo');
通常我们会把声明语句放到一个单独的文件(jQuery.d.ts
)中,这就是声明文件,声明文件必需以.d.ts
为后缀。
一般来说,ts 会解析项目中所有的*.ts
文件,当然也包含以.d.ts
结尾的文件,假如仍然无法解析,那么可以检查下tsconfig.json
中的files
、include
和exclude
配置,确保其包含了jQuery.d.ts
文件。import foo from 'foo'
导入一个 npm 包
npm 包的声明文件可能存在于两个地方:
- 与该 npm 包绑定在一起。判断依据是
package.json
中有types
字段,或者有一个index.d.ts
声明文件。这种模式不需要额外安装其他包,是最为推荐的,所以以后我们自己创建 npm 包的时候,最好也将声明文件与 npm 包绑定在一起。 - 发布到
@types
里。我们只需要尝试安装一下对应的@types
包就知道是否存在该声明文件,安装命令是npm install @types/foo --save-dev
。这种模式一般是由于 npm 包的维护者没有提供声明文件,所以只能由其他人将声明文件发布到@types
里了。
假如以上两种方式都没有找到对应的声明文件,那么我们就需要自己为它写声明文件了。由于是通过 import
语句导入的模块,所以声明文件存放的位置也有所约束,一般有两种方案:
- 创建一个
node_modules/@types/foo/index.d.ts
文件,存放foo
模块的声明文件。这种方式不需要额外的配置,但是node_modules
目录不稳定,代码也没有被保存到仓库中,无法回溯版本,有不小心被删除的风险,故不太建议用这种方案,一般只用作临时测试。 - 创建一个
types
目录,专门用来管理自己写的声明文件,将foo
的声明文件放到types/foo/index.d.ts
中。这种方式需要配置下tsconfig.json
中的paths
和baseUrl
字段。
目录结构:
/path/to/project
├── src
| └── index.ts
├── types
| └── foo
| └── index.d.ts
└── tsconfig.json
tsconfig.json
内容:
{ "compilerOptions": { "module": "commonjs", "baseUrl": "./", "paths": { "*": ["types/*"] } } }
只有function
、class
和interface
可以直接默认导出,其他的变量需要先定义出来,再默认导出
,需要使用 declare enum
定义出来,然后使用 export default
导出:
// types/foo/index.d.ts declare enum Directions { Up, Down, Left, Right } export default Directions;
export =
在 commonjs 规范中,我们用以下方式来导出一个模块:
// 整体导出 module.exports = foo; // 单个导出 exports.bar = bar;
在 ts 中,针对这种模块导出,有多种方式可以导入,第一种方式是 const ... = require
:
// 整体导入 const foo = require('foo'); // 单个导入 const bar = require('foo').bar;
第二种方式是 import ... from
,注意针对整体导出,需要使用 import * as
来导入:
// 整体导入 import * as foo from 'foo'; // 单个导入 import { bar } from 'foo';
第三种方式是 import ... require
,这也是 ts 官方推荐的方式:
// 整体导入 import foo = require('foo'); // 单个导入 import bar = foo.bar;
对于这种使用 commonjs 规范的库,假如要为它写类型声明文件的话,就需要使用到 export =
这种语法了21:
// types/foo/index.d.ts export = foo; declare function foo(): string; declare namespace foo { const bar: number; }
需要注意的是,上例中使用了 export =
之后,就不能再单个导出 export { bar }
了。所以我们通过声明合并,使用 declare namespace foo
来将 bar
合并到 foo
里。
既可以通过 <script>
标签引入,又可以通过 import
导入的库,称为 UMD 库。相比于 npm 包的类型声明文件,我们需要额外声明一个全局变量,为了实现这种方式,ts 提供了一个新语法 export as namespace
。
export as namespace
ECMAScript 标准提供的内置对象有:
Boolean
、Error
、Date
、RegExp
等。
DOM 和 BOM 提供的内置对象有:
Document
、HTMLElement
、Event
、NodeList
等。
标签:string,ts,number,let,类型,foo From: https://www.cnblogs.com/selfimprove/p/17330576.html