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 defaultrequire->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