typescript_基础
type
, interface
, 泛型(泛化的类型)
tsc
$ tsc --outDir dirName $ tsc --outDir dirName compileName # 指定输出输入位置 $ tsc --init # tsconfig.json $ tsc -w # 动态监视 $ ts-node file # 直接运行ts文件
$ npm bin -g # 查看-g命令目录 $ tsc file -d # 生成.d.ts文件
tsconfig.json
tsconfig.json
是编译上下文,其中的compilerOptions
字段提供编译选项,可以通过tsc --init
生成
Typescript作用:
- 类型检查(静态类型,强类型)
- 更好的避免bug
- 自带文档特性
- IDE或编辑器良好支持(自动完成提示)
Typescript = JavaScript + type + (some other stuff)
types
变量使用前要定义
// let/var/const 变量名:变量类型 = 默认值 let test: string = 'test'
变量类型
number
: 数值类型string
: 字符串类型boolean
: 布尔类型symbol
: 符号类型,标识唯一对象any
: 任意类型object
: 对象类型(数组Array<string>
,元祖[string, number]
,类class
,接口interface
,函数function
等)
Boolean Number String Array Tuple(解构) Enum(集中对数值方面进行命名) interface(面向对象) class(面向对象) Any Void Null Undeinfed Never(不可返回,函数中一定会发生异常或无限循环)
any void boolean number string null undefined string[] /* or Array<string> */ [string, number] /* tuple */ string | null | undefined /* union */ never /* unreachable */
enum Color {Red, Green, Blue = 4} let c: Color = Color.Green
类型别名
类型别名常用于联合类型
type Name = string | string[] type StrOrNum = string | number // 使用 let sample: StrOrNum
字符串字面量类型
类型别名与字符串字面量类型只能用type
关键字定义
type EventNames = 'click' | 'scroll' | 'mousemove' function handleEvent (ele: Element, event: EventNames) { // do something } handleEvent(document.getElementById('hello'), 'click'); // 没问题 handleEvent(document.getElementById('world'), 'dbclick'); // 报错,event 不能为 'dbclick'
var,let 区别
- 限定变量的作用范围
- 防止变量的重复定义
常量
用处:
- 系统配置文件路径
- 数据库连接串
- 公司名称,电话,邮件地址
- 画面表示信息(登录失败,系统出错)
const name:type = initial_value const DATA:number[] = [10, 20, 30]
数组
let name:type[] = initial_value // 类型 + 方括号 let name: Array<number> = [12, 2, 23] // 数组泛型 Array<elemType> let name:type[][] = [ // 二维数组 [], [], [] ] interface NumberArray { // 用接口表示数组 [index: number]: number } let name: NumberArray = [12, 2, 23] function sum () { // 类数组 (常见的类数组都有接口定义,如 IArguments, NodeList, HTMLCollection) let args: IArguments = arguments; }
枚举
枚举类型,可以增加代码的可读性。
enum name { name1, name2, name3 } enum Sex { MALE, FEMALE, UNKNOWN }
联合类型
let a: number | null | undefined
内置对象
let b: Boolean = new Boolean(1) let e: Error = new Error('Error occurred') let d: Date = new Date() let r: RegExp = /[a-z]/
DOM 和 BOM 的内置对象
Document
、HTMLElement
、Event
、NodeList
、MouseEvent
let body: HTMLElement = document.body let allDiv: NodeList = document.querySelectorAll('div') document.addEventListener('click', function(e: MouseEvent) { // Do something });
声明类型:
未声明类型默认当作any
类型
建议:声明类型成员类型
建议:声明方法/函数的参数类型和返回类型
能明确推导时:局部变量可以不声明类型(局部变量通常不需要明确定义类型,尽可能使用类型推导)
能明确推导时:函数箭头可以不声明类型。(只有可以参数,箭头函数可以不实用括号,但是返回值需要的话,就需要加上括号)
function
function add (a: number, b: number): number { return a + b } // 返回类型可选 function add (a: number, b: number) { ... }
function run(a: string): string { return '' } let s = function (a: number): string {} let t1 = (x, y) => x + y let t2 = (x, y) => { return x + y } let t3:(a: number, b: string) => void = function (a: number, b: string): void {} interface P { (a: number, b: string): void } let add: P = function (a: number, b: string): void {}
在TS
的类型定义中,=>
用来表示函数的定义,左边是输入类型,需要用括号括起来,右边是输出类型。
let mySum: (x: number, y: number) => number = function (x: number, y: number): number { return x + y; };
函数重载
通过为同一个函数(同名函数)提供多个函数类型定义来实现多种功能的目的。
需多次声明函数头,最后一个函数头是在函数体内实现函数体,但不可以用于外部。
// 重载 function padding(all: number) function padding(topAndBottom: number, leftAndRight: number) function padding(top: number, right: number, bottom: number, left: number) function padding(a: number, b?: number, c?: number, d?: number) { if (b === undefined && c === undefined && d === undefined) { b = c = d = a } else if (c === undefined && d === undefined) { c = a d = b } return { top: a, right: b, bottom: c, left: d } }
class
静态属性,静态方法
class Point { x: number y: number static instances = 0 constructor(x: number, y: number) { this.x = x this.y = y } } class Person { public name: string static age: number constructor (name: string) { this.name = name } public run () { console.log('run') } static work () { // 静态方法里没方法调用成员方法 console.log('work') } } let p = new Person('s')
声明类成员:
成员变量必须声明了才能使用。(赋值,取值)。
在私有成员需要提前在类中声明。
抽象类,多态
多态:父类定义一个方法不去实现,让继承的子类去实现,每一个子类有不同的表现。
// 多态 class Animal { protected name: string constructor (name: string) { this.name = name } public eat () { console.log(this.name + ' eat') } } class Dog extends Animal { constructor (name: string) { super(name) } public eat () { console.log(this.name + ' eat') } } class Pig extends Animal { constructor (name: string) { super(name) } public eat () { console.log(this.name + ' eat') } public hoho () { console.log('hoho') } } let d = new Dog('dog') d.eat() let p = new Pig('pig') p.hoho()
// 抽象类: 定义一种标准 abstract class Animal { abstract eat (): void abstract name: string } class Dog extends Animal { public name = 'dog' constructor () { super() } public eat () {} }
interface
接口运行时的影响为 0
声明空间:
类型声明空间与变量声明空间
类型注解: 不能当作变量来使用,因为它没有定义在变量声明。
class Person {} type Bas = {} interface Test {}
变量声明:变量仅在声明空间中使用,不能用作类型注解。
内联
function printLabel (options: { label: string }) { console.log(options.label) } // 注意分号 function getUser (): { name: string; age?: number } { }
显式
interface LabelOptions { label: string } function printLabel(options: LabelOptions) { ... }
可选属性
interface User { name: string, age?: number }
只读
interface User { readonly name: string }
动态key
{ [key: string]: Object[] }
接口是规范的定义。
// 属性类型接口 interface Color { firstName: string name: string } let a: Color = { // 对象约束 name: 'tan', firstName: 'pink' }
// 函数类型接口 interface encrypt { (key: string, val: string): string } let md5: encrypt = (key, val): string => {} const simple: (foo: number) => string = foo => foo.toString() // 可索引接口,数组的约束 interface UserArr { [index: number]: string } let arr:UserArr = ['a', 'b'] // 可索引接口,对象的约束 interface UserObj { [index: string]: string } let obj: UserObj = { name: 's' } // 类接口约束, 对类的约束, 实现接口类 interface Animate { name: string eat(e: string): void } class Pig implements Animate { name: string constructor () {} eat () {} }
type与interface
能用interface
实现,就用interface
, 如果不能就用type
相同点:
- 都可以描述一个对象或者函数
- 都允许拓展(extends): 并且两者并不是相互独立的,
interface
可以extends type
,type
也可以extends interface
// interface interface User { name: string age: number } interface SetUser { (name: string, age: number): void } // type type User = { name: string age: number } type SetUser = { (name: string, age: number): void } // interface extends interface interface Name { name: string } interface User extends Name { age: number } // type extends type type Name = { name: string } type User = Name & { age: number } // interface extends type type Name = { name: string } interface User extends Name { age: number } // type extends interface interface Name { name: string } type User = Name & { age: number }
不同点:
type
可以声明基本类型别名,联合类型,元组等类型interface
能够声明合并
// 基本类型别名 type Name = string // 联合类型 interface Dog { wong(); } interface Cat { miao(); } type Pet = Dog | Cat // 具体定义数组每个位置的类型 type PetList = [Dog, Pet] // 获取一个变量的类型时,使用typeof, 如果不存在,则获取该类型的推论类型 let div = document.createElement('div') type B = typeof div interface Person { name: string; age: number; location?: string; } const jack: Person = { name: 'jack', age: 100 }; type Jack = typeof jack; // -> Person function foo(x: number): Array<number> { return [x]; } type F = typeof foo; // -> (x: number) => number[] // 类型推导 // interface 声明合并 // 定义相同的接口,合并成新的接口描述 interface User { name: string age: number } interface User { sex: string }
generics
参数化的类型,一般用来限制集合的内容。
任何出现类型的地方都可以使用
泛 -- 宽泛
作用:解决类,接口,方法的复用性,以及对不特定数据类型的支持(类型校验)
当使用简单的泛型时,泛型常用T
, U
, V
表示。如果在参数里,不止拥有一个泛型,那应该使用更加语义化的名称。例如:Tkey
和TValue
// 泛型函数 function getData<T> (value: T): T { return value } // 泛型类 class Greeter<T> { greeting: T constructor(message: T) { this.greeting = message } } let greeter = new Greeter<string>('Hello, world') // 接口泛型 interface Array<T> { reverse(): T[] sort(compare?: (a:T, b:T) => number): T[] } // 接口函数泛型 interface ConfingFn { <T>(val1: T, val2: T): T } interface ConfingFn<T> { (val1: T, val2: T): T }
类型断言
作用:显式指定变量值的类型。
使用方法:在需要断言的变量前加上<Type>
类型断言并不是类型转换,断言成一个联合类型中不存在的类型时不允许的。
let len: number = (input as string).length let len: number = (<string> input).length /* 不能再 JSX 中使用 */
多个类型参数
function swap<T, U>(tuple: [T, U]): [U, T] { // 一次定义多个类型参数 return [tuple[1], tuple[0]]; } swap([7, 'seven']); // ['seven', 7] // 用来交换输入的元组
other
使用风格
- 箭头函数代替匿名函数表达式
x => x + x
(x, y) => x + y
<T>(x: T, y: T) => x === y
- 使用
{}
把循环体和条件语句括起来for (let i = 0; i < 10; i++) {}
if (x < 10) {}
- 每一个变量声明语句只声明一个变量。
let x = 1; let y = 1;
并不是let x = 1, y = 1
- 如果函数没有返回值,使用
void
- 需要的时候才把箭头函数的参数括起来。
处理json和字符串
let person = '{"name": "pink", "age": 22}' const jsonParse: ((key: string, value: any) => any) | undefined = undefined let objectConverted = JSON.parse(person, jsonParse)
转换为Number, String
TS
中推荐使用Number()
, String()
Number('10') // 10 String(10) // '10'
对象属性不存在错误
- 能修改该值的类型声明,那么添加上缺损值的属性
- 使用
// @ts-ignore
- 使用类型断言,强制为
any
类型:(this.props as any).flag
类型不明确的错误
优先使用类型保护。
- 使用类型保护(
type guards
) - 使用类型断言
- 使用
// @ts-ignore
注释
声明文件
当使用第三方库时, 需要引用它的声明文件。
约定俗成的声明文件以.d.ts
为后缀
AlloyTeam团队 的 TSLint 配置
npm install --save-dev tslint-config-alloy
安装之后修改
{ "extends": "tslint-config-alloy", "rules": { // 这里填入项目需要的个性化配置,比如: // // 一个缩进必须用两个空格替代 // "indent": [ // true, // "spaces", // 2 // ] }, "linterOptions": { "exclude": [ "**/node_modules/**" ] } }
ES6修改成TS
- 声明类成员,声明类型,变量,函数,类(识别类型的意识)
- 模块语法
- 添加
TypeScript
配置文件tsconfig.json
- 改写
webpack.config.js
使.ts
编译成.js
/.js -> /.ts module.exports
->export
或者export default
require
->import
- 在类中需要定义私有属性
- 第三方库
@types/xxx
- 在函数中需要泛型,也需要默认值,需要使用重载
- 类型定义
- 自定义类型或使用对象字面量,使用
interface
- 为函数,参数,返回值,类属性添加类型注解,函数中的局部变量通过类型推导。常用:
number []
,class
,interface
,boolean
,string
更改构建脚本:
module: { rules: [ { test: /\.ts/, use: [ { loader: 'babel-loader', options: { presets: ['es2015'] } }, 'ts-loader' ] exclued: /node_modules/ } ] }
在函数中需要泛型,也需要默认值:
class MarixToolkit { mackRow(): number[]; mackRow<T>(v: T): T[]; mackRow(v: any = 0): any[] { return new Array(9) } }
自定义类型或使用对象字面量,使用interface
:
interface IBoxCoord { boxIndex: number cellIndex: number }
总结:封装,继承,模块,方法,重载,接口
封装
- 使用
class
关键词来声明类 - 使用
constructor
关键字来声明构造函数 - 使用
private
,protected
关键字来控制访问权限 - 可以使用
get
/set
来实现私有成员访问器 - 支持静态属性
static
class Person { privated _Name: string privated _Sex: boolean constructor (Name: string, Sex: boolean) { this._Name = Name this._Sex = Sex } get Name () { return this._Name } set Name (Name: string) { this._Name = Name } get Sex () { return this._Sex } set Sex (Sex: boolean) { this._Sex = Sex } static SayHello (person: Person) { return 'hello world' } } let p = new Person('t', true) Person.SayHello(p)
继承
- 使用
extends
关键字完成继承 - 使用
super
关键字来访问父类
class Person { privated _Name: string privated _Sex: boolean constructor (Name: string, Sex: boolean) { this._Name = Name this._Sex = Sex } } class Student extends Person { private _Grade: string get _Grade () { return _Grade } set _Grade (grade: string) { return this._Grade = grade } constructor (Name: string, Sex: boolean) { super(Name, Sex) } } let p = new Person('s', true) let s = new Student('t', false)
模块
- 模块的概念等同于命名空间
- 使用
module
关键字来声明模块 - 在模块中默认成员都是私有的
- 在模块中使用
export
关键字声明共有资源 - 模块支持跨文件分隔
- 在
Node.js/Require.js
中使用require
关键字导入模块 - 可以使用
import
关键字来声明模块的别名
定义:
module com.test { export class Person { privated _Name: string privated _Sex: boolean constructor (Name: string, Sex: boolean) { this._Name = Name this._Sex = Sex } get Name () { return this._Name } set Name (Name: string) { this._Name = Name } get Sex () { return this._Sex } set Sex (Sex: boolean) { this._Sex = Sex } static SayHello (person: Person) { return 'hello world' } } }
使用:
import test = com.test // 目录
方法
- 方法是
JavaScript
中一个特殊的对象 - 方法分为命名方法和匿名方法
- 方法类型
- 方法推断类型
- 方法参数可以定义默认值
- 方法参数可以使用
...
去定义可变参数组
function add (n1, n2) { return n1 + n2 } let add1 = function (n1, n2) { // 数据类型存在 return n1 + n2 } let sum = function (n1: number, n2: number): number { return n1 + n2 } let sum1: (num1: number, num2: number) => number = function (num1: number, num2: number): number { return num1 + num2 } let sum2: (num1: number, num2: number) => number = function (x, y) { return x + y } function sum3 (n1: number = 5, n2: number = 10, ...num2: Array<number>) { return n1 + n2 }
重载
JavaScript中本身不支持重载。
在TypeScript中使用重载的方法:
- 先声明出所有的方法重载的定义,不包含方法实现。
- 再声明出一个参数为
any
类型的重载方法。 - 实现
any
类型的方法,并通过参数类型不同来去实现重载。
使用了泛型下也需要默认值:
class MarixToolkit { mackRow(): number[] // 定义 mackRow<T>(v: T): T[] // 定义 mackRow(v: any = 0): any[] { // 实现 return new Array(9) } }
接口
在接口中可以包含属性,方法。
使用interface
关键字定义接口。
属性可以使用?
标示允许为空类型。
TypeScript支持隐式接口
接口可以标示为方法类型
接口可以多继承
可以使用<接口类型>{}
来去隐式声明匿名对象实现接口
interface Count { (num1: number, num2: number): number } let add: Count = function (a, b) { return a + b }
interface Count { num1: number num2: number } let _count = <Count>{} _count.num1 = 10 _count.num2 = 100