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, 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, 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 = [12, 2, 23] // 数组泛型 Array
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
内置对象
TS核心库定义文件
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 {
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 (value: T): T {
return value
}
// 泛型类
class Greeter {
greeting: T
constructor(message: T) {
this.greeting = message
}
}
let greeter = new Greeter('Hello, world')
// 接口泛型
interface Array {
reverse(): T[]
sort(compare?: (a:T, b:T) => number): T[]
}
// 接口函数泛型
interface ConfingFn {
(val1: T, val2: T): T
}
interface ConfingFn {
(val1: T, val2: T): T
}
类型断言
作用:显式指定变量值的类型。
使用方法:在需要断言的变量前加上
类型断言并不是类型转换,断言成一个联合类型中不存在的类型时不允许的。
let len: number = (input as string).length
let len: number = ( input).length /* 不能再 JSX 中使用 */
多个类型参数
function swap(tuple: [T, U]): [U, T] { // 一次定义多个类型参数
return [tuple[1], tuple[0]];
}
swap([7, 'seven']); // ['seven', 7] // 用来交换输入的元组
other
使用风格
- 箭头函数代替匿名函数表达式
x => x + x
(x, y) => x + y
(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 配置
tslint-config-alloy
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(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) {
return n1 + n2
}
重载
JavaScript中本身不支持重载。
在TypeScript中使用重载的方法:
- 先声明出所有的方法重载的定义,不包含方法实现。
- 再声明出一个参数为
Any
类型的重载方法。 - 实现
Any
类型的方法,并通过参数类型不同来去实现重载。
使用了泛型下也需要默认值:
class MarixToolkit {
mackRow(): number[] // 定义
mackRow(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.num1 = 10
_count.num2 = 100