https://ts.xcatliu.com

简介

什么是 TypeScript

  • 即使不显式的定义类型,也能够自动做出类型推论
  • 即使第三方库不是用 TypeScript 写的,也可以编写单独的类型文件供 TypeScript 读取
  • 接口(Interfaces)、泛型(Generics)、类(Classes)、枚举类型(Enums)?

安装 TypeScript

  • Typescript 库用来对 ts 进行编译npm install -g typescript
  • 主流的编辑器都支持 TypeScript,利用它的特点实现代码补全、接口提示、跳转到定义、重构等功能。

Hello TypeScript

  • 使用 : 指定变量的类型,: 的前后有没有空格都可以。function sayHello(person: string) { }
  • TypeScript 只会进行静态检查,如果发现有错误,编译的时候就会报错,但是还是生成了 js 文件。编译后的代码不会插入类型检查的代码
  • 如果要在报错的时候终止 js 文件的生成,可以在 tsconfig.json 中配置 noEmitOnError 即可。

基础

原始数据类型

  • let isDone: boolean = false;
  • 使用构造函数 Boolean 创造的对象不是布尔值
  • 直接调用 Boolean 也可以返回一个 boolean 类型let createdByBoolean: boolean = Boolean(1);
  • 使用 number 定义数值类型
let decLiteral: number = 6;
let hexLiteral: number = 0xf00d;
let notANumber: number = NaN;
let infinityNumber: number = Infinity; // ES6 中的二进制表示法,编译为:var binaryLiteral = 10;
let binaryLiteral: number = 0b1010; // ES6 中的八进制表示法,编译为:var octalLiteral = 484;
let octalLiteral: number = 0o744;
  • 用 void 表示没有任何返回值的函数,注意它跟 undefined 和 null 的区别
function alertName(): void {
alert('My name is Tom');
}
  • undefined 和 null 是所有类型的子类型,可以赋值给任意类型let num: number = undefined;而 void 类型不行

任意值

  • any 类型,允许被赋值为任意类型let myFavoriteNumber: any = 'seven';
  • 变量如果在声明的时候,未指定其类型,那么它会被识别为任意值类型

类型推论

  • 如果没有明确的指定类型,那么 TypeScript 会依照类型推论(Type Inference)的规则推断出一个类型。
  • 如果定义的时候没有赋值,不管之后有没有赋值,都会被推断成 any 类型(指可以赋值为任意值,但是赋值后依然能自动推断类型,并提供智能提示)

联合类型

  • let myFavoriteNumber: string | number;
  • 当 TypeScript 不确定一个联合类型的变量到底是哪个类型的时候,只能访问联合类型里共有的属性或方法
  • 联合类型的变量在被赋值的时候,会根据类型推论的规则推断出一个类型。就可以使用该类型的特有属性和方法

对象的类型——接口

  • 这种方式在给变量赋值时并不能出现该接口应有参数的提醒,但可以通过报错查看相关信息
  • 接口(Interfaces)用来定义对象的类型,而具体如何行动需要由类(classes)去实现(implement)。?
interface Person { // 接口一般首字母大写
name: string; // 这里使用;或,都行
age: number;
} let tom: Person = {
name: 'Tom',
age: 25
}; // 可以写为
let tom: {
name:string,
ccc:number
} = {
name: 'Tom',
ccc:123
};
  • 定义的变量和接口不一致是不允许的
  • 可选属性,即使定义为该类型的对象没有这个属性依然会出现对应的提醒。
interface Person {
name: string;
age?: number;
} let tom: Person = {
name: 'Tom'
};
  • 允许有任意的属性,这个任意属性不能够被提醒
interface Person {
name: string;
[propName: string]: any;
} let tom: Person = {
name: 'Tom',
gender: 'male'
};
  • 一旦定义了任意属性,那么确定属性和可选属性的类型都必须是它的类型的子集。
interface Person {
name: string;
age?: number;
[propName: string]: string | number; // 这里的例子是错误的,因为这里的任意属性的类型只是 string | number 其中的一种,不是两种,所以不能够包含确定和属性的所有类型。
}
  • 只读属性,只读的约束存在于第一次给对象赋值的时候,而不是第一次给只读属性赋值的时候
interface Person {
readonly id: number; // 只能在创建的时候被赋值
name: string;
age?: number;
[propName: string]: any;
}

数组的类型

  • 使用「类型 + 方括号」来表示数组let fibonacci: number[] = [1, 1, 2, 3, 5];
  • 数组的一些方法的参数也会根据数组在定义时约定的类型进行限制
let fibonacci: number[] = [1, 1, 2, 3, 5];
fibonacci.push('8'); // 不能插入字符串
  • 使用数组泛型(Array Generic) Array 来表示数组let fibonacci: Array<number> = [1, 1, 2, 3, 5];(另外一种定义类型的方法)
  • 用接口表示数组,常用来表示类数组
interface NumberArray {
[index: number]: number; // 理解为:索引的类型是数字时,那么值的类型必须是数字
}
let fibonacci: NumberArray = [1, 1, 2, 3, 5]; function sum() {
let args: { // 用来表示类数组
[index: number]: number;
length: number;
callee: Function; // 函数类型的表示比较特别,用大写的
} = arguments;
}
  • 常用的类数组 TypeScript 中都定义好了的类型,如 IArguments, NodeList, HTMLCollection 等(内置对象)。let args: IArguments = arguments;

函数的类型

  • 一个函数有输入和输出,要在 TypeScript 中对其进行约束。
  • 函数声明的定义方式:function sum(x: number, y: number): number { }
  • 输入多余的(或者少于要求的)参数,是不被允许的
  • 函数表达式定义方式:本质上是声明一个变量为函数
let mySum: (x: number, y: number) => number = function (x: number, y: number): number {
return x + y;
}; // 箭头函数的方式
const mySum:(x:number,y:number)=>number = (x:number,y:number):number=>{
return x+y
}
  • 可选参数,可选参数必须接在必需参数后面function buildName(firstName: string, lastName?: string) { }
  • 参数默认值function buildName(firstName: string, lastName: string = 'Cat') { }
  • ES6 带默认值的参数传入 undefined 时代表使用默认值
function buildName(firstName: string = 'Tom', lastName: string):string {
return firstName + ' ' + lastName;
}
let tomcat = buildName('Tom', 'Cat');
let cat = buildName(undefined, 'Cat');
  • 剩余参数(es6 中剩余参数只能是最后一个参数)
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:any){ // 官方的写法是 function reverse(x: number | string): number | string { } 感觉有点累赘
if (typeof x === 'number') {
return Number(x.toString().split('').reverse().join(''));
} else {
return x.split('').reverse().join('');
}
} // 多参数
function reverse(x: number,y:number): number;
function reverse(x: string): string;
function reverse(x: any,y?:any) {
if(typeof y === 'number'){
return Number(x)+y
}else{
return x
}
} // 箭头函数的重载?

断言

  • 将一个联合类型的变量指定为一个更加具体的类型,断言成一个联合类型中不存在的类型是不允许的
function getLength(something: string | number): number {
if ((something as string).length) {
return (something as string).length;
} else {
return something.toString().length;
}
}

声明文件

  • 声明文件

    • declare var 声明全局变量,也可以是 declare let 和 declare const
    • declare function 声明全局函数
    • declare class 声明全局类
    • declare enum 声明全局枚举类型(用来声明某个对象下的常量属性,例如:Math.PI)
    • declare namespace 声明(含有子属性的)全局对象(和 接口 类似,但是更加丰富可以包含其他声明)
    • interface 和 type 声明全局类型(interface 声明全局接口,type 声明全局类型) type 与 interface 类似 ?(全局似乎不对)
    • export 导出变量(export 是导入式声明的方法和 declare 全局的方式相同可以和其他关键字组合,例如:export function getName(): string;
    • export namespace 导出(含有子属性的)对象
    • export default ES6 默认导出
    • export = commonjs 导出模块
    • export as namespace UMD 库声明全局变量 ?
    • declare global 扩展全局变量 ?
    • declare module 扩展模块 ?
    • /// 三斜线指令 ?
  • 通常我们会把声明语句放到一个单独的文件(jQuery.d.ts)中,声明文件必需以 .d.ts 为后缀。(.d.ts 后缀被视为环境上下文和 .ts 不同,它不允许初始化值只允许提供类型定义)

  • ts 会解析项目中所有的 *.ts 文件,当然也包含以 .d.ts 结尾的文件。(tsconfig.json 中的 files、include 和 exclude 配置决定了解析范围)

  • 全局变量模式的声明文件

  • 使用 declare const 定义时,表示此时的全局变量是一个常量,不允许再去修改它的值

// src/jQuery.d.ts

declare var jQuery: (selector: string) => any;
  • 使用 @types 统一管理第三方库的声明文件npm install @types/jquery --save-dev(vscode有插件能自动管理)

  • 使用 https://microsoft.github.io/TypeSearch/ 搜索某个库的声明

  • 库的使用场景

    • 全局变量:通过 <script> 标签引入第三方库,注入全局变量
    • npm 包:通过 import foo from 'foo' 导入,符合 ES6 模块规范
    • UMD 库:既可以通过 <script> 标签引入,又可以通过 import 导入
    • 直接扩展全局变量:通过 <script> 标签引入后,改变一个全局变量的结构
    • 在 npm 包或 UMD 库中扩展全局变量:引用 npm 包或 UMD 库后,改变一个全局变量的结构
    • 模块插件:通过 <script> 或 import 导入后,改变另一个模块的结构
  • 声明语句中只能定义类型,切勿在声明语句中定义具体的实现

  • 在函数类型的声明语句中,函数重载也是支持的

declare function jQuery(selector: string): any;
declare function jQuery(domReadyCallback: () => any): any; // 这里 domReadyCallback 使用的是变量声明,被声明为函数
  • 当全局变量是一个类的时候,我们用 declare class 来定义它的类型
declare class Animal {
name: string; // 申明实例的属性
constructor(name: string); // 申明构造器的传参
sayHi(): string; // 申明实力方法的传参和返回
}
  • 声明文件里的内容在编译结果中会被删除,仅仅会用于编译时的检查
  • 使用 declare enum 定义的枚举类型也称作外部枚举(Ambient Enums)(用来声明对象下的常量,例如:Math.PI)
declare enum Directions {
Up,
Down,
Left,
Right
}
  • namespace 是 ts 早期时为了解决模块化而创造的关键字,中文称为命名空间。曾用名 module ,现都被 ES6 的 module 淘汰。
  • declare namespace 还是比较常用的,它用来表示全局变量是一个对象,包含很多子属性。可以使用 const, class, enum,namespace 等语句
declare namespace jQuery {
function ajax(url: string, settings?: any): void;
const version: number;
class Event {
blur(eventType: EventType): void
}
enum EventType {
CustomClick
}
namespace fn { // 嵌套的命名空间
function extend(object: any): void;
}
}
  • 假如 jQuery 下仅有 fn 这一个属性(没有 ajax 等其他属性或方法),则可以不需要嵌套 namespace(嵌套的命名空间的另外一种写法)
declare namespace jQuery.fn {
function extend(object: any): void;
}
  • 暴露在最外层的 interface 或 type 会作为全局类型作用于整个项目中,我们应该尽可能的减少全局变量或全局类型的数量。故最好将他们放到 namespace 下,在使用这个 interface 的时候,也应该加上前缀?(在模块化代码中 interface 并不会被暴露到全局中,需要在每个模块中单独定义,这里的说法有问题)
declare namespace jQuery {
interface AjaxSettings {
method?: 'GET' | 'POST' // 声明可以指定固定字符串
data?: any;
}
function ajax(url: string, settings?: AjaxSettings): void;
} let settings: jQuery.AjaxSettings = {
method: 'POST',
data: {
name: 'foo'
}
};
jQuery.ajax('/api/post_something', settings);
  • 可以组合多个声明语句,它们会不冲突的合并起来
declare function jQuery(selector: string): any;
declare namespace jQuery {
function ajax(url: string, settings?: any): void;
}
  • 一般来说,npm 包的声明文件可能存在于两个地方:package.json 中有 types 字段(如何使用?),或者有一个 index.d.ts 声明文件(该声明文件在 package.json 中通过 typings 指明路径)。
  • 自己为 import 语句导入的模块写声明文件的方法:
    • 创建一个 node_modules/@types/foo/index.d.ts 文件,存放 foo 模块的声明文件。这种方式不需要额外的配置,不推荐
    • 创建一个 types 目录,专门用来管理自己写的声明文件,将 foo 的声明文件放到 types/foo/index.d.ts 中。这种方式需要配置下 tsconfig.json 中的 paths 和 baseUrl 字段。(在 @vue/cli 项目中实际测试应该在 include 添加)
{
"compilerOptions": {
"module": "commonjs",
"baseUrl": "./",
"paths": {
"*": ["types/*"]
}
}
} // 在 @vue/cli 项目中实际测试应该在 include 添加
{
"compilerOptions": {
"target": "esnext",
"module": "esnext",
"strict": true,
"jsx": "preserve",
"importHelpers": true,
"moduleResolution": "node",
"experimentalDecorators": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"sourceMap": true,
"baseUrl": ".",
"types": [
"webpack-env"
],
"paths": {
"@/*": [
"src/*"
]
},
"lib": [
"esnext",
"dom",
"dom.iterable",
"scripthost"
]
},
"include": [
"src/**/*.ts",
"src/**/*.tsx",
"src/**/*.vue",
"tests/**/*.ts",
"tests/**/*.tsx",
"types/**/*.ts" // 为 import 文件编写的 .d.ts 文件
],
"exclude": [
"node_modules"
]
}
  • tsconfig.json 中的 module 配置可以有很多种选项,不同的选项会影响模块的导入导出模式

  • npm 包的声明文件主要有以下几种语法:(应该称为导入式声明包的语法,应该是在 import 包时跟随引入,由包 package.json 中的 typings 设置了引入地址,就如同由 main 设置 import 的文件地址一样)(@types 独立声明中由 types 指明引入地址,好像编辑器会自动包含这个库的 .d.ts 声明)

    • export 导出变量
    • export namespace 导出(含有子属性的)对象
    • export default ES6 默认导出
    • export = commonjs 导出模块
  • 在 npm 包的声明文件中,使用 declare 不再会声明一个全局变量,而只会在当前文件中声明一个局部变量。只有在声明文件中使用 export 导出,然后在使用方 import 导入后,才会应用到这些类型声明。

  • 导入式使用声明(npm 库应该是隐性的实现了这个过程)

// foo.d.ts
export const name: string;
export function getName(): string; // .ts
import { name, getName } from 'foo';
let myName = getName(); // getName 已经在其他地方全局注入了,这里只是引入对它的声明,然后就可以实现校验和提示了
  • 可以使用 declare 先声明多个变量,最后再用 export 一次性导出。
// ... 其他interface
interface Options {
data: any;
} export { name, getName, Animal, Directions, Options }; // 一次性导出,es6 的写法
  • 只有 function、class 和 interface 可以直接默认导出,其他的变量需要先定义出来,再默认导出
// ERROR: Expression expected.
export default enum Directions { // 枚举直接作为默认导出会报错
Up,
Down,
Left,
Right
} // 正确方式
export default Directions; // 一般会将导出语句放在整个声明文件的最前面 declare enum Directions {
Up,
Down,
Left,
Right
}
  • commonjs 规范中的导出和导入方法
// 导出
module.exports = foo; // 整体导出
exports.bar = bar; // 单个导出 // 导入
// 整体导入
const foo = require('foo'); // commonjs
import * as foo from 'foo'; // ES6
import foo = require('foo'); // ts 官方推荐
// 单个导入
const bar = require('foo').bar; // commonjs
import { bar } from 'foo'; // ES6
import bar = foo.bar; // ts 官方推荐
  • 对于使用 commonjs 规范的库,假如要为它写类型声明文件的话,就需要使用到 export = 这种语法

TypeScript(进行中)的更多相关文章

  1. 在 Ionic2 TypeScript 项目中导入第三方 JS 库

    原文发表于我的技术博客 本文分享了在Ionic2 TypeScript 项目中导入第三方 JS 库的方法,供参考. 原文发表于我的技术博客 1. Typings 的方式 因在 TypeScript 中 ...

  2. 在TypeScript项目中进行BDD测试

    在TypeScript项目中进行BDD测试 什么是BDD? BDD(Behavior-Driven Design)是软件团队的一种工作方式,通过以下方式缩小业务人员和技术人员之间的差距: 鼓励跨角色协 ...

  3. Vue+Typescript项目中使用echarts

    方案一:推荐 在typescript+Vue的项目中引用echarts,为了加强引用,引入echarts和@types/echarts两个包,一个是工程依赖,一个是声明依赖. npm install ...

  4. typescript项目中import 图片时报错:TS2307: Cannot find module ‘...’

    最近在用typescript写项目时,我用import来加载一个图片,webpack编译文件是会报错如下: 报错: 解决: 如果在js中引入本地静态资源图片时使用import img from './ ...

  5. 在typeScript+vue项目中使用ref

    因为vue项目是无法直接操作dom的,但是有时候开发需求迫使我们去操作dom. 两个办法,一个是很low的再引入jq,然后通过jq来操作,但是这样就失去了我们使用vue的意义, 可惜的是我曾经这样干过 ...

  6. 【转载】在Angular 2/Typescript中声明全局变量的最佳方式是什么?

    问题详细描述 我想在Typescript语言中的Angular 2中声明一些全局可见的变量.最佳的实践方法是? 推荐的实现方法 这是最简单的解决方案,无需使用Service或Observer: 将全局 ...

  7. 在typescript中import第三方类库clipboard报错

    一.问题 在实际开发项目中就遇到了这样的问题,需要在Vue+Typescript项目中添加复制文本的功能,就找了clipboard插件,先是新建了一个新的项目用来实验看看是否好用,都写好了以后发给别人 ...

  8. TypeScript中使用getElementXXX()

    如果只是看解决方法,可以直接跳到第二小节 简述 Angular 1.x版本是用JavaScript编写的,我们在百度Angular经常会搜索到AngularJS,并不是JavaScript的什么衍生版 ...

  9. TypeScript 中函数的理解?与 JavaScript 函数的区别?

    一.是什么 函数是JavaScript 应用程序的基础,帮助我们实现抽象层.模拟类.信息隐藏和模块 在TypeScript 里,虽然已经支持类.命名空间和模块,但函数仍然是主要定义行为的方式,Type ...

  10. Vue 中使用 TypeScript 详细总结

    VUE 项目中使用 Typescript 第一节:项目起步 Vue 中使用 TypeScript 项目中主要使用到的第三方依赖 vue2 vue-class-component vue-propert ...

随机推荐

  1. Linux 下tomcat 的重新启动

    在 Linux 系统下,重启 Tomcat 如何使用命令操作? 1.首先,进入 Tomcat 的 bin 目录 cd /usr/local/tomcat/bin 2.使用Tomcat关闭命令 ./sh ...

  2. 遇到的一些在ie下的兼容问题和解决方案(ie10+)

    1,ie 10下实现水平垂直居中,不固定高度的话,正常的top:50%,left:50%,transform(translate(-50%,-50%)) 是不能实现的,ie下top:50%会失去效果. ...

  3. python 迭代器和生成器详解

    一.迭代器 说迭代器之前有两个相关的名词需要介绍:可迭代对象:只要定义了__iter__()方法,我们就说该对象是可迭代对象,并且可迭代对象能提供迭代器.迭代器:实现了__next__()或者next ...

  4. Ceph 13.2.8 三节点部署

    bs-k8s-ceph eth1 mon osd mgr deploy 2c2g sdb sdc sdd 各20G bs-hk-hk01 eth1 mon osd mgr 2c2g sdb sdc s ...

  5. 关于线段树的感悟(Segment Tree)

    线段树的感悟 : 学过的东西一定要多回头看看,不然真的会忘个干干净净. 线段树的 Introduction : English Name : Segment Tree 顾名思义 : 该数据结构由两个重 ...

  6. qt creator源码全方面分析(2-3)

    目录 External Tool Specification Files 文件名 位置 文件格式 主要标签 描述标签 可执行规范标签 示例 External Tool Specification Fi ...

  7. ELK 记录 java log4j 类型日志

    ELK 记载  java log4j 时,一个报错会生成很多行,阅读起来很不方便. 类似这样 解决这个问题的方法 1.使用多行合并 合并多行数据(Multiline) 有些时候,应用程序调试日志会包含 ...

  8. const与指针、引用

    const与指针类型 定义一个指针*p: const int* p = NULL; int const* p = NULL; int* const p = NULL; 上面两行定义完全等价,第三行则不 ...

  9. openlayers6实现webgl点图层渲染效果(附源码下载)

    前言:openlayers6推出来的有一段时间,推出来的新特性见:https://github.com/openlayers/openlayers/releases/该版本的主要功能是能够组合具有不同 ...

  10. SSM项目下Druid连接池的配置及数据源监控的使用

    一,连接池的配置 在pom.xml中添加,druid的maven信息 <dependency> <groupId>com.alibaba</groupId> < ...