一篇入门TypeScript
date
May 9, 2022
slug
12
status
Published
tags
Javascript
Typescript
summary
简单了解TypeScript
type
Post
Book
Basic-types
Javascript中的类型集合
ECMAScript定义Javascript语言中类型集合由原始值(primitive values)和对象(Object)组成
- primitive values:
- boolean
- null
- BigInt
- undefined
- string
- number
- symbol
- Object
typescript中声明一个类型
//声明一个布尔类型的变量
let isDone: boolean = false
isDone = 123 //会报错
let message: string = `Hello, ${firstName}` //不会报错undefined和null是所有类型的子集
let num: number = undefined //不会报错当不确定变量类型时可以用any声明
let notSure: any = 4
notSure = 'maybe a string'
notSure = true
notSure.myName
notSure.getName()Array and Tuple
声明数组类型
let arrOfNumbers: number[] = [1,2,3]
arrOfNumbers.push('string') //报错JS内置类
function test() {
//JS内置类
console.log(arguments)
let htmlCollections: HTMLAllCollection = undefined
}声明元组
let user: [string, number] = ['viking', 20] //顺序不能反
//元组也是一个数组,可以使用数组的方法
user.push(20)
//但只能添加原来定义的类型
user.push(true) //报错Interface
声明接口(Interface)
interface IPerson {
name: string;
age: number;
}
//少了属性会报错
let viking: IPerson = {
name: 'viking'
}
//多了属性也会报错
let hiking: IPerson = {
name: 'hiking',
age: 10,
id: 123
}Interface可选属性
interface IPerson {
name: string;
age?: number;
}
//不会报错
let viking: IPerson = {
name: 'viking'
}Interface只读属性
interface IPerson {
readonly id: number;
name: string;
age: number;
}
let hiking: IPerson = {
name: 'hiking',
age: 10,
id: 123
}
hiking.id = 567 //报错Function
Function声明
function add(x: number, y: number): number {
return x + y
}
add(1, '123') // 因为y必须是number类型,所以报错
let isString: string = add(1,2) //因为add()的返回值必须是number类型,所以报错Function可选参数
function add(x: number, y: number, z?:number): number {
return x + y + z
}
add(1,2) //因为z可选,所以不会报错
//可选参数后面不能再加必选参数,所以报错
function add1(x: number, y: number, z?: number, h: number): number {
return x + y + z
}声明函数表达式
const add = (x: number, y: number, z?:number): number => {
return x + y + z
}
//add的类型是(x: number, y: number, z?:number) => number
let add1: string = add //报错,因为add的类型不是string类型
let add2: (x: number, y: number, z?:number)=>number = add //不会报错Interface描述函数类型
const add = (x: number, y: number, z?:number): number => {
return x + y + z
}
interface ISum {
(x: number, y: number, z?:number): number
}
let add2: ISum = add //不会报错类型推论、联合类型和类型断言
什么是类型推论?
let str = 'str' //str被推测为string类型
str = 123 //报错,因为str被推测为string类型联合类型
let numberOrString: number | string
//不会报错
numberOrString = 123
numberOrString = 'abc'当一个联合类型的变量没有确定是哪个类型的时候,只能访问此联合类型所有类型的共有属性或方法
let numberOrString: number | string
numberOrString.length //会报错,因为number类型上没有length属性
numberOrString.toString() //不会报错,因为toString()方法是共有方法类型断言
类型断言只能够欺骗TypeScript编译器,无法避免运行时的错误。
function getLength(input: string | number): number {
const str = input as string //欺骗编译器input是string类型
if (str.length){
return str.length
}else {
console.log(str.length) //undefined
return str.toString().length
}
}类型保护
类型保护就是TypeScript通过JavaScript中的
instanceof和typeof运算符的用法,推导出在条件块中的变量类型,从而允许你使用更小范围下的对象类型。function getLength2(input: string | number): number {
if (typeof input === 'string'){
//此时input已经被推断为stirng类型
return input.length
} else {
//此时input已经被推断为number类型
return input.toString().length
}
}关于
class和instanceof的例子:class Foo {
foo = 123;
common = '123';
}
class Bar {
bar = 123;
common = '123';
}
function doStuff(arg: Foo | Bar) {
if (arg instanceof Foo) {
console.log(arg.foo); // ok
console.log(arg.bar); // Error
}
if (arg instanceof Bar) {
console.log(arg.foo); // Error
console.log(arg.bar); // ok
}
}Class
默认是Public修饰,所以不作解释
Private
private用来修饰成私有属性,私有属性只能在类的内部访问
class Animal {
name: string;
constructor(name) {
this.name = name
}
private run() {
return `${this.name} is running`
}
}
const snake = new Animal('lily')
console.log(snake.run()) //报错Protected
protected可以被继承,在派生类访问。但父类和子类都不能通过实例访问
class Animal {
name: string;
constructor(name) {
this.name = name
}
protected run() {
return `${this.name} is running`
}
}
const snake = new Animal('lily')
console.log(snake.run()) //报错,实例不能调用protected修饰的方法
class Cat extends Animal{
//不会报错,子类可以调用父类protected修饰的方法。
run() {
return 'Meow, '+ super.run()
}
}Readonly
readonly修饰只读属性
class Animal {
readonly name: string;
constructor(name) {
this.name = name
}
protected run() {
return `${this.name} is running`
}
}
const snake = new Animal('lily')
snake.name = '123' //报错,因为name是readonlyClass and Implement
类和接口
有时候不同的类有共同的特性,需要声明规范这个共有特性时又不方便找一个父类去继承。用接口就可以实现这样的功能。
interface Radio{
switchRadio(trigger: boolean): void
}
class Car implements Radio{
//报错,因为没有实现switchRadio方法
}
class Phone {
switchRadio(trigger: boolean) {
}
}多个接口
interface Radio{
switchRadio(trigger: boolean): void;
}
interface Battery {
checkBatteryStatus(): void;
}
class Phone implements Radio,Battery {
switchRadio(trigger: boolean) {}
checkBatteryStatus(){}
}接口也可以继承
interface Radio{
switchRadio(trigger: boolean): void;
}
interface RadioWithBattery extends Radio{
checkBatteryStatus(): void;
}
class Car implements RadioWithBattery {
//报错,因为没有实现switchRadio方法
checkBatteryStatus(){}
}
class Phone implements RadioWithBattery {
switchRadio(trigger: boolean) {}
checkBatteryStatus(){}
}枚举
数字枚举
enum direction {
//数字枚举会自动从0开始赋值
Up,
Down,
Right,
Left
}
console.log(direction.Up) //0
console.log(direction.Down) //1
//也可以通过下标来访问
console.log(direction[0]) //Up自定义赋值
从第一项递增
enum direction {
Up = 10,
Down, //11
Right, //12
Left //13
}如果自定义赋值,则下标访问也要用对应的值
enum direction {
Up = 10,
Down, //11
Right, //12
Left //13
}
console.log(direction[1]) //undefined
console.log(direction[11]) //Down值可以相同,但
字符串枚举
enum direction {
Up = 'Up',
Down = 'Down',
Right = 'Right',
Left = 'Left'
}
console.log(direction.Up) //Up字符串和数值枚举混用,字符串后面必须初始化值。
enum direction {
Up = 'Up',
Down = 'Down',
Right = 'Right',
Left //报错
}enum direction {
Left, //不报错
Up = 'Up',
Down = 'Down',
Right = 'Right',
LeftDown =
}
console.log(direction.Left) //0常量枚举
const enum direction {
Up = 'Up',
Down = 'Down',
Right = 'Right',
Left = 'Left'
}常量枚举可以提高性能
/* const_enum.ts */
const enum direction {
Up = 'Up',
Down = 'Down',
Right = 'Right',
Left = 'Left'
}
const value = 'Up';
if(value === direction.Up){
console.log('go up!!')
}/* const_enum.js */
//编译后的js文件
var value = 'Up';
if (value === "Up" /* Up */) {
console.log('go up!!');
}并不是所有枚举都能用作常量枚举。计算值枚举就不能用作常量枚举。
泛型(generics)
在定义函数、接口或类的时候,不预先指定具体的类型,而在使用的时候再指定类型的一种特性。
简单例子
function echo(arg) {
return arg
}
const result = echo(true) //此时result的类型是any,没有准确定义返回值的类型。
//因为我输入的是boolean类型,我希望能够准确定义返回值也是boolean类型。
const result1 = echo('123') //这里我希望result1的类型也能准确定义为string类型,但事实上不是。/* 使用泛型来实现 */
function echo<T>(arg: T): T {
return arg
}
const result = echo(true) //此时result的类型是true,也就是boolean类型。和输入值的类型是一致的。多个类型
function swap<T,U>(tuple: [T, U]): [U,T] {
return [tuple[1], tuple[0]]
}
const result = swap(['string', 123]) //此时result的返回值为[number,string]泛型约束
当我们想要限制函数去处理带有length属性的所有类型时,我们就需要用到泛型约束。简单例子:
function echoWithLength<T>(arg: T): T {
console.log(arg.length) //会报错,因为T类型没有length属性
return arg
}
const arrNumber = echoWithLength([1, 2, 3])function echoWithLength<T>(arg: T[]): T[] {
console.log(arg.length) //此时不会报错
return arg
}
const arrNumber = echoWithLength([1, 2, 3])
const str = echoWithLength('str') //会报错,因为str不能赋值到[]通过泛型约束来解决:
interface IWithLength {
length: number
}
function echoWithLength<T extends IWithLength>(arg: T): T {
console.log(arg.length)
return arg
}
const arrNumber = echoWithLength([1, 2, 3])
const obj = echoWithLength({ length: 10, id: 1000 }) //不会报错,因为有length属性
const str = echoWithLength('str')
const num = echoWithLength(123) //报错,因为number类型没有length属性泛型类
泛型类允许定义类时约束类的类型定义:
/* 没有约束类型,编译前不会报错,编译时会报错 */
class Queue {
private data = []
push(item) {
return this.data.push(item)
}
pop() {
return this.data.shift()
}
}
const queue = new Queue()
queue.push(1)
queue.push('str')
console.log(queue.pop().toFixed())
console.log(queue.pop().toFixed()) //因为string类型没有toFixed()函数/* 通过泛型约束 */
class Queue<T> {
private data: = []
push(item: T) {
return this.data.push(item)
}
pop(): T {
return this.data.shift()
}
}
const queue = new Queue<number>()
queue.push(1)
queue.push('str') //报错,因为push函数约束了参数为number类型。泛型接口
interface KeyPair<T,U> {
key: T,
value: U
}
const kp1: KeyPair<number, string> = { key:1, value: 'string'}
const kp2: KeyPair<string, number> = { key:'1', value: 1 }泛型接口也可以这样用:
/* 两者效果一样 */
let arr: number[] = [1,2,3]
let arr1: Array<number> = [1,2,3]类型别名
顾名思义就是给一个类型起个新的名字
type stringOrnumber = string | number
//都不会报错
let num: stringOrnumber = 1
let str: stringOrnumber = 'str'字面量
字面量类型用来约束取值只能是某几个中的一个。
type direction = 'Up' | 'Down' | 'Right' | 'Left'
let toWhere: direction = 'Up'实用例子
type EventNames = 'click' | 'scroll' | 'mousemove';
function handleEvent(ele: Element, event: EventNames) {
// do something
}
handleEvent(document.getElementById('hello'), 'scroll'); // 没问题
handleEvent(document.getElementById('world'), 'dblclick'); // 报错,event 不能为 'dblclick'交叉属性
可以对接口进行拓展
interface IName {
name: string
}
type IPerson = IName & { age: number }
let person: IPerson = { name: 'lewis', age: 123}声明文件
当使用第三方库时,我们需要引进它的声明文件,才能获得对应的代码补全、接口提示功能。
什么是声明语句
假如我们想使用第三方库 jQuery,一种常见的方式是在 html 中通过
<script> 标签引入 jQuery,然后就可以使用全局变量 $ 或 jQuery 了。我们通常这样获取一个
id 是 foo 的元素:$('#foo');
// or
jQuery('#foo');但是在 ts 中,编译器并不知道
$ 或 jQuery 是什么东西:jQuery('#foo');
// ERROR: Cannot find name 'jQuery'.这时,我们需要使用
declare var 来定义它的类型:declare var jQuery: (selector: string) => any;
jQuery('#foo');
上例中,
declare var 并没有真的定义一个变量,只是定义了全局变量 jQuery 的类型,仅仅会用于编译时的检查,在编译结果中会被删除。它编译结果是:jQuery('#foo');什么是声明文件
通常我们会把声明语句放到一个单独的文件(
jQuery.d.ts)中,这就是声明文件:/* jQuery.d.ts */
declare var jQuery: (selector: string) => any;/* index.ts */
jQuery('#foo');声明文件必需以
.d.ts为后缀。