TypeScript 是 JavaScript 的类型的超集,它能够编译成纯 JavaScript。编译出来的 JavaScript 能够运行在任何浏览器上。TypeScript 编译工具能够运行在任何服务器和任何系统上。javascript
JavaScript是一门弱类型/动态类型脚本语言。过于灵活,没有类型检查。在大型项目中代码愈来愈复杂,JavaScript这种灵活的优点就变成了短板html
TypeScript 的命令行工具安装方法以下:前端
npm install -g typescript
复制代码
以上命令会在全局环境下安装 tsc 命令,安装完成以后,咱们就能够在任何地方执行 tsc 命令了。java
注意:mac 的话 须要加上
sudo
node
sudo npm install -g typescript
复制代码
Hello TypeScriptreact
首先建立一个01-ts.ts
.ts文件git
将如下代码复制到 01-ts.ts 中:es6
const hello = name => {
console.log(`hello, ${name}`) } hello ('TS') 复制代码
而后执行github
tsc 01-ts.ts
复制代码
这时候会生成一个编译好的文件 01-ts.ts:web
var hello = function (name) {
console.log("hello, " + name); }; hello('TS'); 复制代码
TypeScript 中,使用 : 指定变量的类型,: 的先后有没有空格均可以。
上述例子中,咱们用 : 指定name 参数类型为 string。可是编译为 js 以后,并无什么检查的代码被插入进来。
TypeScript 只会进行静态检查,若是发现有错误,编译的时候就会报错。
const hello = (name:string) => {
console.log(`hello, ${name}`) } hello (1) 复制代码
编辑器中会提示错误,编译的时候也会出错:
01-ts.ts:5:8 - error TS2345: Argument of type '1' is not assignable to parameter of type 'string'.
5 hello (1) ~ 复制代码
就算出现错误仍是会生成js文件:
var hello = function (name) {
console.log("hello, " + name); }; hello(1); 复制代码
开始使用 tsconfig.json 是一件比较容易的事,你仅仅须要写下:
{}
复制代码
或者使用 tsc --init
建立tsconfig.json
在项目的根目录下建立一个空 JSON 文件。经过这种方式,TypeScript 将 会把此目录和子目录下的全部 .ts 文件做为编译上下文的一部分,它还会包含一部分默认的编译选项。
你能够经过 compilerOptions 来定制你的编译选项:
{
"compilerOptions": { /* 基本选项 */ "target": "es5", // 指定 ECMAScript 目标版本: 'ES3' (default), 'ES5', 'ES6'/'ES2015', 'ES2016', 'ES2017', or 'ESNEXT' "module": "commonjs", // 指定使用模块: 'commonjs', 'amd', 'system', 'umd' or 'es2015' "lib": [], // 指定要包含在编译中的库文件 "allowJs": true, // 容许编译 javascript 文件 "checkJs": true, // 报告 javascript 文件中的错误 "jsx": "preserve", // 指定 jsx 代码的生成: 'preserve', 'react-native', or 'react' "declaration": true, // 生成相应的 '.d.ts' 文件 "sourceMap": true, // 生成相应的 '.map' 文件 "outFile": "./", // 将输出文件合并为一个文件 "outDir": "./", // 指定输出目录 "rootDir": "./", // 用来控制输出目录结构 --outDir. "removeComments": true, // 删除编译后的全部的注释 "noEmit": true, // 不生成输出文件 "importHelpers": true, // 从 tslib 导入辅助工具函数 "isolatedModules": true, // 将每一个文件作为单独的模块 (与 'ts.transpileModule' 相似). /* 严格的类型检查选项 */ "strict": true, // 启用全部严格类型检查选项 "noImplicitAny": true, // 在表达式和声明上有隐含的 any类型时报错 "strictNullChecks": true, // 启用严格的 null 检查 "noImplicitThis": true, // 当 this 表达式值为 any 类型的时候,生成一个错误 "alwaysStrict": true, // 以严格模式检查每一个模块,并在每一个文件里加入 'use strict' /* 额外的检查 */ "noUnusedLocals": true, // 有未使用的变量时,抛出错误 "noUnusedParameters": true, // 有未使用的参数时,抛出错误 "noImplicitReturns": true, // 并非全部函数里的代码都有返回值时,抛出错误 "noFallthroughCasesInSwitch": true, // 报告 switch 语句的 fallthrough 错误。(即,不容许 switch 的 case 语句贯穿) /* 模块解析选项 */ "moduleResolution": "node", // 选择模块解析策略: 'node' (Node.js) or 'classic' (TypeScript pre-1.6) "baseUrl": "./", // 用于解析非相对模块名称的基目录 "paths": {}, // 模块名到基于 baseUrl 的路径映射的列表 "rootDirs": [], // 根文件夹列表,其组合内容表示项目运行时的结构内容 "typeRoots": [], // 包含类型声明的文件列表 "types": [], // 须要包含的类型声明文件名列表 "allowSyntheticDefaultImports": true, // 容许从没有设置默认导出的模块中默认导入。 /* Source Map Options */ "sourceRoot": "./", // 指定调试器应该找到 TypeScript 文件而不是源文件的位置 "mapRoot": "./", // 指定调试器应该找到映射文件而不是生成文件的位置 "inlineSourceMap": true, // 生成单个 soucemaps 文件,而不是将 sourcemaps 生成不一样的文件 "inlineSources": true, // 将代码与 sourcemaps 生成到一个文件中,要求同时设置了 --inlineSourceMap 或 --sourceMap 属性 /* 其余选项 */ "experimentalDecorators": true, // 启用装饰器 "emitDecoratorMetadata": true // 为装饰器提供元数据的支持 } } 复制代码
配置文件配好后 就能够直接使用tsc
进行编译
在默认状况下,当你开始在一个新的 TypeScript 文件中写下代码时,它处于全局命名空间中。如在 foo.ts 里的如下代码。
const foo = 123;
复制代码
若是你在相同的项目里建立了一个新的文件 bar.ts,TypeScript 类型系统将会容许你使用变量 foo,就好像它在全局可用同样:
const bar = foo; // allowed
复制代码
毋庸置疑,使用全局变量空间是危险的,由于它会与文件内的代码命名冲突。咱们推荐使用下文中将要提到的文件模块
解决这个咱们只须要在文件最后 使用export {}
这样每一个文件就是一个模块,单独的做用域
// 数字,2、8、十六进制都支持
let num:number =0 let num2:number=0xf00a // 字符串 let v:string="a" //let name:string="liu" // 这里会提示 'name' was also declared here // 缘由是在默认状态下,typescript 将 DOM typings 做为全局的运行环境,因此当咱们声明 name时, 与 DOM 中的全局 window 对象下的 name 属性出现了重名。 // const a:string=null // 这里是在tsconfig.json 配置了严格模式 "strict": true 改为false 就行了 const c:boolean=false // void只能存放null/undefined 注意:在严格模式下只能是 undefined const d:void=undefined const f:null=null const g:undefined=undefined // 这里报错 'Symbol' only refers to a type, but is being used as a value here. Do you need to change your target library? Try changing the `lib` compiler option to es2015 or later. //是由于Symbol是es2015才有的 ts使用的标准库没有 在tsconfig.json设置 "lib": ["es2015","DOM"] //之后遇到内置方法报错的时候可能就是标准库的关系 在在tsconfig.json设置 "lib" 就行了 const h:symbol=Symbol() 复制代码
const foo:object = {}//能够是 function (){} //[]//{}
const obj:{foo:number,bar:string}={foo:1,bar:'w'} // 这里只是简单的定义,跟专业的须要用接口来定义 export { } //确保跟其余成员没有冲突 复制代码
// 写法1
const arr1:Array<number>=[1,2,3] // 写法2 const arr2:number[]=[1,2,3] let arr3:string[] = ["1","2"] let arr4:Array<string> = ["1","2"] // 小案例 // 若是是 JS,须要判断是否是每一个成员都是数字 // 使用 TS,类型有保障,不用添加类型判断 function sum (...args: number[]) { return args.reduce((prev, current) => prev + current, 0) } sum(1, 2, 3) // => 6 复制代码
// 元组(Tuple)
export {} // 确保跟其它示例没有成员冲突 const tuple: [number, string] = [18, 'zce'] // const age = tuple[0] // const name = tuple[1] const [age, name] = tuple // --------------------- const entries: [string, number][] = Object.entries({ foo: 123, bar: 456 }) const [key, value] = entries[0] // key => foo, value => 123 复制代码
枚举是对JavaScript标准数据类型集的扩充,常被用来限定在必定范围内取值的场景,如一周只能有七天,颜色限定为红绿蓝等。串的枚举。咱们能够用enum来实现。
// 枚举
// 注意 enum 是采用 = 赋值 // enum PostStatus { // Draft = 0, // Unpublished = 1, // Published = 2 // } // 字符串枚举 // enum PostStatus { // Draft = "a", // Unpublished = "b", // Published = "c" // } //---------------------------- // 能够不用赋值 默认从0开始 // enum PostStatus { // Draft , //0 // Unpublished , //1 // Published //2 // } //---------------------------- // 给第一个赋值 后面的会在第一个基础上自加 // enum PostStatus { // Draft =6, //6 // Unpublished , //7 // Published //8 // } 复制代码
// 常量枚举
const enum PostStatus { Draft , //0 Unpublished , //1 Published //2 } const post ={ status:PostStatus.Draft } // 编译后 var post = { status: 1 /* Draft */ }; 复制代码
我一般用 = 1 初始化,由于在枚举类型值里,它能让你作一个安全可靠的检查。
// 简单案例
// function func1 (a: number, b: number): string { // return 'func1' // } // func1(100, 200) // 默认值或者能够选参数 // b: number=1 // b?: number 问号标示可选 // function func1 (a: number, b: number=1): string { // return 'func1' // } // func1(100, 200) // 任意个数的参数 使用es6扩展运算符 // function func1 (...res:number): string { // return 'func1' // } // func1(100, 200) 复制代码
// 任意类型(弱类型)
export {} // 确保跟其它示例没有成员冲突 function stringify (value: any) { return JSON.stringify(value) } stringify('string') stringify(100) stringify(true) let foo: any = 'string' foo = 100 foo.bar() // any 类型是不安全的 复制代码
若是没有明确的指定类型,那么 TypeScript 会依照类型推论(Type Inference)的规则推断出一个类型。
let foo = 123; // foo 是 'number'
let bar = 'hello'; // bar 是 'string' foo = bar; // Error: 不能将 'string' 赋值给 `number` //数组 const bar = [1, 2, 3]; bar[0] = 'hello'; // Error:不能把 'string' 类型赋值给 'number' 类型 //对象 const foo = { a: 123, b: 456 }; foo.a = 'hello'; // Error:不能把 'string' 类型赋值给 'number' 类型 复制代码
建议尽可能每一个变量都添加明确的类型,便于后期更直观的理解代码
若是类型不能被赋值推断出来,类型也将不会流入函数参数中。例如以下的一个例子,编译器并不知道 foo 的类型,所它也就不能推断出 a 或者 b 的类型。
const foo = (a, b) => {
/* do something */ }; 复制代码
然而,若是 foo 添加了类型注解,函数参数也就能被推断(a,b 都能被推断为 number 类型):
type TwoNumberFunction = (a: number, b: number) => void;
const foo: TwoNumberFunction = (a, b) => { /* do something */ }; 复制代码
尽管 TypeScript 通常状况下能推断函数的返回值,可是它可能并非你想要的。例如以下的 foo 函数,它的返回值为 any:
function foo(a: number, b: number) {
return a + addOne(b); } // 一些使用 JavaScript 库的特殊函数 function addOne(a) { return a + 1; } 复制代码
这是由于返回值的类型被一个缺乏类型定义的 addOne 函数所影响(a 是 any,因此 addOne 返回值为 any,foo 的返回值是也是 any)。
在某些状况TS没法检测出类型,须要你告诉它。TypeScript 类型断言用来告诉编译器你比它更了解这个类型,而且它不该该再发出错误
// 类型断言
export {} // 确保跟其它示例没有成员冲突 // 假定这个 nums 来自一个明确的接口 const nums = [110, 120, 119, 112] const res = nums.find(i => i > 0) // res TypeScript 是不知道是什么类型的 全部须要你告诉它 // const square = res * res // 推荐使用 as const num1 = res as number const num2 = <number>res // JSX 下不能使用 复制代码
注意: 类型断言不是类型转换,是由于转换一般意味着某种运行时的支持。可是,类型断言纯粹是一个编译时语法,同时,它也是一种为编译器提供关于如何分析代码的方法。
在面向对象语言中,接口(Interfaces)是一个很重要的概念,它是对行为的抽象,而具体如何行动须要由类(classes)去实现(implement)。
// 接口
interface Post { title:string content:string } // 使用接口 function printPost (post: Post) { console.log(post.title) console.log(post.content) } printPost({title:'234',content:'23'}) 复制代码
// ? 标示可选 // interface Post { // title:string // content:string, // subtitle?:string // } // // 使用接口 // function printPost (post: Post) { // console.log(post.title) // console.log(post.content) // } // readonly 只读 interface Post { title:string content:string, subtitle?:string, readonly sun:string } // 使用接口 function printPost (post: Post) { console.log(post.title) console.log(post.content) } // 任意类型的属性 interface Post { [prop:string]:string } // 使用接口 const cache:Post={ name:'sdffs', title:'s' } 复制代码
export { }
class Person { // 首先先定义类型 name: string age: string constructor(name: string, age: string) { this.name = name this.age = age } sayHi(msg:string):void{ console.log(`${this.name}${msg}`); } } 复制代码
class Person {
// 首先先定义类型 public name: string //公共 private age: string // 私有 protected gender: string // constructor(name: string, age: string) { this.name = name this.age = age this.gender = 'sss' } sayHi(msg: string): void { console.log(`${this.name}${msg}`); } } class Student extends Person { constructor(name: string, age: string) { super(name, age) console.log(this.gender) // 能够访问到 } } const tom = new Person('tom', '18') // console.log(tom.age); //访问不到 // console.log(tom.gender); //也访问不到 复制代码
constructor 加修饰符
class Student extends Person {
private constructor (name: string, age: number) { super(name, age) console.log(this.gender) } static create (name: string, age: number) { return new Student(name, age) } } const tom = new Person('tom', 18) console.log(tom.name) // console.log(tom.age) // console.log(tom.gender) //不能直接经过 new来创造对象 const jack = Student.create('jack', 18) 复制代码
使用`readonly`来定义只读,若是有修饰符的话 跟在修饰符后面
class Person { // 首先先定义类型 public name: string //公共 private age: string // 私有 // 只读成员 readonly protected readonly gender: boolean constructor(name: string, age: string) { this.name = name this.age = age this.gender = 'sss' } sayHi(msg: string): void { console.log(`${this.name}${msg}`); } } 复制代码
实现(implements)是面向对象中的一个重要概念。通常来说,一个类只能继承自另外一个类,有时候不一样类之间能够有一些共有的特性,这时候就能够把特性提取成接口(interfaces),用 implements 关键字来实现。这个特性大大提升了面向对象的灵活性。
举例来讲,门是一个类,防盗门是门的子类。若是防盗门有一个报警器的功能,咱们能够简单的给防盗门添加一个报警方法。这时候若是有另外一个类,车,也有报警器的功能,就能够考虑把报警器提取出来,做为一个接口,防盗门和车都去实现它:
export{} // 先定义接口 interface Eat { // 定义方法类型 eat (food:string):void } interface Run { // 定义方法类型 run (distance:string):void } class Person implements Eat,Run{ eat(food:string):void{ console.log(food); } run (distance:string):void { console.log(distance); } } 复制代码
使用abstract
来定义 抽象类
abstract class Animal {
eat (food: string): void { console.log(`呼噜呼噜的吃: ${food}`) } // 抽象方法不须要方法体 abstract run (distance: number): void } class Dog extends Animal { run(distance: number): void { console.log('四脚爬行', distance) } } const d = new Dog() d.eat('嗯西马') d.run(100) 复制代码
不能直接经过 new Animal() 建立实例
泛型(Generics)是指在定义函数、接口或类的时候,不预先指定具体的类型,而在使用的时候再指定类型的一种特性。
// 泛型
export {} // 确保跟其它示例没有成员冲突 function createNumberArray (length: number, value: number): number[] { const arr = Array<number>(length).fill(value) return arr } function createStringArray (length: number, value: string): string[] { const arr = Array<string>(length).fill(value) return arr } function createArray<T> (length: number, value: T): T[] { const arr = Array<T>(length).fill(value) return arr } // const res = createNumberArray(3, 100) // res => [100, 100, 100] const res = createArray<string>(3, 'foo') 复制代码
有一些三方的库 引入不知道是什么类型 可使用declare
lai 声明类型
还有些第三方库会有专门的类型声明文件
import { camelCase } from 'lodash'
import qs from 'query-string' qs.parse('?key=value&key2=value2') // declare function camelCase (input: string): string const res = camelCase('hello typed') 复制代码
参考