TypeScript(进行中)
简介
什么是 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(进行中)的更多相关文章
- 在 Ionic2 TypeScript 项目中导入第三方 JS 库
		
原文发表于我的技术博客 本文分享了在Ionic2 TypeScript 项目中导入第三方 JS 库的方法,供参考. 原文发表于我的技术博客 1. Typings 的方式 因在 TypeScript 中 ...
 - 在TypeScript项目中进行BDD测试
		
在TypeScript项目中进行BDD测试 什么是BDD? BDD(Behavior-Driven Design)是软件团队的一种工作方式,通过以下方式缩小业务人员和技术人员之间的差距: 鼓励跨角色协 ...
 - Vue+Typescript项目中使用echarts
		
方案一:推荐 在typescript+Vue的项目中引用echarts,为了加强引用,引入echarts和@types/echarts两个包,一个是工程依赖,一个是声明依赖. npm install ...
 - typescript项目中import 图片时报错:TS2307: Cannot find module ‘...’
		
最近在用typescript写项目时,我用import来加载一个图片,webpack编译文件是会报错如下: 报错: 解决: 如果在js中引入本地静态资源图片时使用import img from './ ...
 - 在typeScript+vue项目中使用ref
		
因为vue项目是无法直接操作dom的,但是有时候开发需求迫使我们去操作dom. 两个办法,一个是很low的再引入jq,然后通过jq来操作,但是这样就失去了我们使用vue的意义, 可惜的是我曾经这样干过 ...
 - 【转载】在Angular 2/Typescript中声明全局变量的最佳方式是什么?
		
问题详细描述 我想在Typescript语言中的Angular 2中声明一些全局可见的变量.最佳的实践方法是? 推荐的实现方法 这是最简单的解决方案,无需使用Service或Observer: 将全局 ...
 - 在typescript中import第三方类库clipboard报错
		
一.问题 在实际开发项目中就遇到了这样的问题,需要在Vue+Typescript项目中添加复制文本的功能,就找了clipboard插件,先是新建了一个新的项目用来实验看看是否好用,都写好了以后发给别人 ...
 - TypeScript中使用getElementXXX()
		
如果只是看解决方法,可以直接跳到第二小节 简述 Angular 1.x版本是用JavaScript编写的,我们在百度Angular经常会搜索到AngularJS,并不是JavaScript的什么衍生版 ...
 - TypeScript 中函数的理解?与 JavaScript 函数的区别?
		
一.是什么 函数是JavaScript 应用程序的基础,帮助我们实现抽象层.模拟类.信息隐藏和模块 在TypeScript 里,虽然已经支持类.命名空间和模块,但函数仍然是主要定义行为的方式,Type ...
 - Vue 中使用 TypeScript 详细总结
		
VUE 项目中使用 Typescript 第一节:项目起步 Vue 中使用 TypeScript 项目中主要使用到的第三方依赖 vue2 vue-class-component vue-propert ...
 
随机推荐
- 构建一个学生Student,根据类Student的定义,创建五个该类的对象,输出每个学生的信息,计算并输出这五个学生Java语言成绩的平均值,以及计算并输出他们Java语言成绩的最大值和最小值。
			
定义一个表示学生信息的类Student,要求如下: (1)类Student的成员变量: sNO 表示学号: sName表示姓名: sSex表示性别: sAge表示年龄: sJava:表示Java课程成 ...
 - Flink 1.10 正式发布!——与Blink集成完成,集成Hive,K8S
			
Apache Flink社区宣布Flink 1.10.0正式发布! 本次Release版本修复1.2K个问题,对Flink作业的整体性能和稳定性做了重大改进,同时增加了对K8S,Python的支持. ...
 - LoadIcon的使用
			
LoadIcon msdn: Loads the specified icon resource from the executable (.exe) file associated with an ...
 - 2018icpc南京现场赛-G Pyramid(打标找规律+逆元)
			
题意: 求n行三角形中等边三角形个数,图二的三角形也算. n<=1e9 思路: 打表找下规律,打表方法:把所有点扔坐标系里n^3爆搜即可 打出来为 1,5,15,35,70,126,210.. ...
 - 程序员过关斩将--redis做消息队列,香吗?
			
Redis消息队列 在程序员这个圈子打拼了太多年,见过太多的程序员使用redis,其中一部分喜欢把redis做缓存(cache)使用,其中最典型的当属存储用户session,除此之外,把redis作为 ...
 - 用赋值表达式作为bool值
			
enum Status { stOk, stQuit, stError }; int main() { Status status; int n; bool b1 = (status = stOk); ...
 - postman之下载文件
			
前言 小伙伴们在实际的测试工作中是否遇到过下载的接口呢,例如网盘的项目就涉及到上传和下载的接口了,那么我们如何利用postman对下载接口进行测试呢?下面我们一起来学习吧! 练习案例:下载接口:htt ...
 - OSPF配置实验(一)
			
单区域OSPF 命令: R1(config)#router ospf 1 //启动OSPF进程 R1(config-router)#router-id 1.1.1.1 // ...
 - 神奇的 SQL 之 WHERE 条件的提取与应用
			
开心一刻 小明:为什么中国人结婚非要选一个好日子呢 ? 楼主:嗯 ? 那肯定啊,结完婚之后你还能有好日子吗 ? 小明:那结婚时所说的白头到老是真的吗 ? 楼主:这哪能是真的,你看现在,头发还没白就秃了 ...
 - NCE L5
			
课文内容 重点单词解析 重点课文解析