javascript强类型入门——TypeScript
- 作者:Bougie
- 创建于:2018-07-15
- 更新于:2023-03-09
上个月 TypeScript 挤进了编程语言排行榜的前五十名。已成为主修 JS 的同学们不可忽视的一门语言。由于 ES6 出现以来 js 已发生翻天覆地的变化,这篇文章主要从 ES6 的角度来一步步阐述 TypeScript。
# 安装
学习期间建议搭配gulp
食用:
npm i gulp gulp-typescript -D
gulpfile.js
配置:
const gulp = require('gulp')
const ts = require('gulp-typescript')
gulp.task('tsc', function () {
let tsResult = gulp.src('src/**/*.ts').pipe(
ts({
noImplicitAny: true
})
)
return tsResult.js.pipe(gulp.dest('dist'))
})
gulp.task('dev', function () {
gulp.watch('src/**/*.ts', ['tsc'])
})
在package.json
中添加:
{
"scripts": {
"dev": "gulp dev"
}
}
运行:
npm run dev
# 基本类型
- 布尔值、数字、字符串
let bol: boolean
let num: number
let str: string
- 数组、元组
// 数组
let list1: number[] = [1, 2, 3]
let list2: Array<number> = [1, 2, 3]
// 多重数组
let list3: number[][][] = [[[1, 2, 3]]]
// 元组,初始化时需类型顺序赋值
let x: [string, number] = ['1', 1]
// 跨界后可任选两种类型的一种
x[3] = 'world'
- 枚举类型
enum Color {
Red,
Green,
Blue
}
let c: Color = Color.Green
console.log(Color, c)
// 声明默认值
enum Colors {
Red = 1,
Green = 3,
Blue = 4
}
let cs: Colors = Colors.Blue
console.log(Colors, cs)
⬇️ 编译后
var Color
;(function (Color) {
Color[(Color['Red'] = 0)] = 'Red'
Color[(Color['Green'] = 1)] = 'Green'
Color[(Color['Blue'] = 2)] = 'Blue'
})(Color || (Color = {}))
var c = Color.Green
console.log(Color, c)
// 声明默认值
var Colors
;(function (Colors) {
Colors[(Colors['Red'] = 1)] = 'Red'
Colors[(Colors['Green'] = 3)] = 'Green'
Colors[(Colors['Blue'] = 4)] = 'Blue'
})(Colors || (Colors = {}))
var cs = Colors.Blue
console.log(Colors, cs)
打印的结果为:
{ '0': 'Red', '1': 'Green', '2': 'Blue', Red: 0, Green: 1, Blue: 2 } 1
{ '1': 'Red', '3': 'Green', '4': 'Blue', Red: 1, Green: 3, Blue: 4 } 4
枚举类型的大概意思是用来生成一种双向的对象,既能根据key
获取value
,也能根据value
获取key
。
- 任意类型
let notSure: any
// 可任意进行类型转换
notSure = 4
notSure = 'str'
// 数组,可为任意类型
let list: any[] = [1, true, 'free']
list[1] = 100
list[1] = 'str'
// 元组
let tuple: [any, number] = [true, 1]
// 首位可为任意类型
tuple[0] = 1
// 跨界后可为任意类型
tuple[2] = 'str'
- 空类型
// 无返回值的函数为Void类型
function returnNothing(): void {
console.log('I return nothing')
}
- null 和 undefined
// 默认情况下null和undefined是所有类型的子类型
let u: undefined = undefined
let n: null = null
let s: string = 'str'
let m: number = 1
s = u
m = n
- never 类型
// 返回never的函数必须存在无法达到的终点
// 即会阻止程序向下运行
function error(message: string): never {
throw new Error(message)
}
// 返回never的函数必须存在无法达到的终点
function infiniteLoop(): never {
while (true) {}
}
# 变量申明
- 使用 let 和 const 代替 var
体验块级作用域的好处
// 输出十个10
for (var i = 0; i < 10; i++) {
setTimeout(function () {
console.log(i)
}, 100 * i)
}
// 输出0到9
for (let i = 0; i < 10; i++) {
setTimeout(function () {
console.log(i)
}, 100 * i)
}
⬇️ 编译后
// 输出十个10
for (var i = 0; i < 10; i++) {
setTimeout(function () {
console.log(i)
}, 100 * i)
}
var _loop_1 = function (i_1) {
setTimeout(function () {
console.log(i_1)
}, 100 * i_1)
}
// 输出0到9
for (var i_1 = 0; i_1 < 10; i_1++) {
_loop_1(i_1)
}
- 变量的结构与赋值
// 基本用法与ES6一样
// 传入Rest参数
let [first, ...rest] = [1, 2, 3, 4]
// 结构并重命名,这里不指定bar的类型会报错,不知道为什么
let { foo: customName }: { foo: string; bar: string } = {
foo: '123',
bar: '456'
}
// 变量交换,很奇怪这里不加分号会报错
let [a, b]: [string, string] = ['a', 'b']
;[b, a] = [a, b]
⬇️ 编译后
// 基本用法与ES6一样
// 传入Rest参数
var _b = [1, 2, 3, 4],
first = _b[0],
rest = _b.slice(1)
// 结构并重命名,这里不指定bar的类型会报错,不知道为什么
var customName = { foo: '123', bar: '456' }.foo
// 变量交换,很奇怪这里不加分号会报错
var _c = ['a', 'b'],
a = _c[0],
b = _c[1]
;(_a = [a, b]), (b = _a[0]), (a = _a[1])
可见可读性增强很多
- 函数默认值
// 函数默认值设置
function keepWholeObject(wholeObject: { a: string; b?: number }) {
let { a, b = 1001 } = wholeObject
}
function f({ a, b } = { a: '', b: 0 }): void {
// ...
}
⬇️ 编译后
// 函数默认值设置
function keepWholeObject(wholeObject) {
var a = wholeObject.a,
_a = wholeObject.b,
b = _a === void 0 ? 1001 : _a
}
function f(_a) {
var _b = _a === void 0 ? { a: '', b: 0 } : _a,
a = _b.a,
b = _b.b
// ...
}
这个与 ES6 的方法不太一样,在 ES6 我们一般:
function fn({ a = 'a', b = 'b' }) {}
babel 编译后
'use strict'
function fn(_ref) {
var _ref$a = _ref.a,
a = _ref$a === undefined ? 'a' : _ref$a,
_ref$b = _ref.b,
b = _ref$b === undefined ? 'b' : _ref$b
}
- 展开操作符
let arr1 = [1, 2]
let arr2 = [3, 4]
let arr3 = [0, ...arr1, ...arr2, 5]
let obj1 = { food: 'spicy', price: '$', ambiance: 'noisy' }
let ojb2 = { ...obj1, food: 'rich' }
⬇️ 编译后
var arr1 = [1, 2]
var arr2 = [3, 4]
var arr3 = [0].concat(arr1, arr2, [5])
// 这里相当于加了个polyfill
var __assign =
(this && this.__assign) ||
Object.assign ||
function (t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i]
for (var p in s)
if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]
}
return t
}
var obj1 = { food: 'spicy', price: '$', ambiance: 'noisy' }
var ojb2 = __assign({}, obj1, { food: 'rich' })
# 接口
- 什么是接口(interface)
interface LabelledValue {
label: string
}
// 接口用来定义一种类型,用法同基本类型
function printLabel(labelledObj: LabelledValue) {
console.log(labelledObj.label)
}
let myObj = { size: 10, label: 'Size 10 Object' }
printLabel(myObj)
- 可选属性
interface SquareConfig {
color?: string
width?: number
}
// 定义可选属性
function createSquare(config: SquareConfig): SquareConfig {
let newSquare = { color: 'white', area: 100 }
if (config.color) {
newSquare.color = config.color
}
if (config.width) {
newSquare.area = config.width * config.width
}
return newSquare
}
let mySquare = createSquare({ color: 'black' })
- 只读属性
interface Point {
readonly x: number
readonly y: number
}
// 定义只读属性
let p1: Point = { x: 10, y: 20 }
// 只读的Array
let ro: ReadonlyArray<number> = [1, 2, 3, 4]
- 函数类型
// 函数类型
interface SearchFunc {
// 参数类型与返回类型
(source: string, subString: string): boolean
}
let mySearch: SearchFunc = function (source: string, subString: string) {
let result = source.search(subString)
return result > -1
}
- 可索引类型
interface StringArray {
[index: number]: string
}
let myArray: StringArray = ['Bob', 'Fred']
let myArrayObj: StringArray = {
1: 'Bob',
2: 'Fred'
}
// 索引必须为数字
let myStr: string = myArray[0]
let myObjStr: string = myArrayObj[1]
- 类类型接口
interface ClockInterface {
currentTime: Date
setTime(d: Date): void
}
// 类类型接口
class Clock implements ClockInterface {
currentTime: Date
setTime(d: Date) {
this.currentTime = d
}
constructor(h: number, m: number) {}
}
⬇️ 编译后
var Clock = /** @class */ (function () {
// currentTime: Date;
function Clock(h, m) {
this.currentTime = new Date(h + m)
}
return Clock
})()
- 接口继承
interface Shape {
color: string
}
interface PenStroke {
penWidth: number
}
interface Square extends Shape, PenStroke {
sideLength: number
}
//一个接口可以继承多个接口,创建出多个接口的合成接口。
let square = <Square>{}
square.color = 'blue'
square.sideLength = 10
square.penWidth = 5.0
- 混合类型
interface Counter {
(start: number): string
interval: number
reset(): void
}
function getCounter(): Counter {
let counter = <Counter>function (start: number) {}
counter.interval = 123
counter.reset = function () {}
return counter
}
let counter = getCounter()
counter(10)
counter.reset()
counter.interval = 5.0
- 接口继承类
class Control {
private state: any
}
interface SelectableControl extends Control {
select(): void
}
class Button extends Control implements SelectableControl {
select() {}
}
class TextBox extends Control {
select() {}
}
⬇️ 编译后
var Control = /** @class */ (function () {
function Control() {}
return Control
})()
// 此处有个实现类继承的polyfill
var __extends =
(this && this.__extends) ||
(function () {
var extendStatics =
Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array &&
function (d, b) {
d.__proto__ = b
}) ||
function (d, b) {
for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]
}
return function (d, b) {
extendStatics(d, b)
function __() {
this.constructor = d
}
d.prototype =
b === null ? Object.create(b) : ((__.prototype = b.prototype), new __())
}
})()
var Button = /** @class */ (function (_super) {
__extends(Button, _super)
function Button() {
return (_super !== null && _super.apply(this, arguments)) || this
}
Button.prototype.select = function () {}
return Button
})(Control)
var TextBox = /** @class */ (function (_super) {
__extends(TextBox, _super)
function TextBox() {
return (_super !== null && _super.apply(this, arguments)) || this
}
TextBox.prototype.select = function () {}
return TextBox
})(Control)
2018-07-15,今天就先到这里了。感觉这东西是不是有些强过头了,。也许使用这东西,二八法则就好。