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. 在cmd中启动tomcat

    E:\Documents and Settings\topicis>h: H:\>cd tomcat-test H:\tomcat-test>cd bin H:\tomcat-tes ...

  2. Servlet乱码问题解决

    对于请求参数的编码处理基本上分为get和post两种情况. 1.POST index.html <!DOCTYPE html> <head> <meta http-equ ...

  3. Qt qApp

    qApp A global pointer referring to the unique application object. It is equivalent to the pointer re ...

  4. golang 自定义结构体(与其他语言对象类似)

    /* 结构体变量: 结构体的定义只是一种内存布局的描述,只有当结构体实例化时,才会真正地分配内存, 因此必须在定义结构体并实例化后才能使用结构体的字段. type 类型名 struct { 字段1 字 ...

  5. 2014.1.21 DNS大事故(dns原理、网络封锁原理)

    1.21那天发生了什么,由1.21联想补充……  很多网站都上不去,域名解析都到了65.49.2.178这个IP地址 先科普,再深挖  dns查询类型 递归查询,迭代查询   DNS解析过程,这里使用 ...

  6. Admin后台权限管理、三大认证

    目录 APIView的请求生命周期 三大认证规则 权限六表 自定义User表 详细配置演示 models.py setting.py admin.py 使用过程: 控制填写信息的字段 控制添加权限 控 ...

  7. pycharm创建Django项目时报 AttributeError:'module' object has no attrbute 'main' 错误或者创建了就只有venv一个目录

    这是因为创建项目时候没有选择合适的项目环境. 所以在创建项目的时候选择一下项目的环境,比如选择python的运行环境 这时候创建的项目就不再报 AttributeError:'module' obje ...

  8. 解决shiro自定义filter后,ajax登录无法登录,并且无法显示静态资源的问题

    这个问题困扰了我一天,看了下面两个文章,豁然开朗: https://www.cnblogs.com/gj1990/p/8057348.html https://412887952-qq-com.ite ...

  9. JavaScript——基础知识,开始我们的js编程之旅吧!

    JavaScript基础第01天 1. 编程语言 编程语言: 可以通过类似于人类语言的"语言"来控制计算机,让计算机为我们做事情,这样的语言就叫做编程语言(Programming ...

  10. springcloud vue.js 前后分离 微服务 分布式 activiti工作流 集成代码生成器 shiro权限

    1.代码生成器: [正反双向](单表.主表.明细表.树形表,快速开发利器)freemaker模版技术 ,0个代码不用写,生成完整的一个模块,带页面.建表sql脚本.处理类.service等完整模块2. ...