ts-playground
ts 中文手册
ts 入门教程
npm i typescript -g
# 初始化
npm init -y
tsc --init
{
"clean-webpack-plugin": "^3.0.0",
"html-webpack-plugin": "^3.2.0",
"ts-loader": "^8.1.0",
"typescript": "^4.2.4",
"webpack": "^4.32.2",
"webpack-cli": "^3.3.2",
"webpack-dev-server": "^3.5.1",
"webpack-merge": "^4.2.1"
}
webpack.base.config.js
npm i webpack webpack-cli webpack-dev-server -D
npm i ts-loader typescript -D
npm i html-webpack-plugin -D
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
entry: {
app: './src/index.ts',
},
output: {
filename: 'app.js',
},
resolve: {
extensions: ['.js', '.tsx', '.ts'],
},
module: {
rules: [
{
test: /\.tsx?$/i,
use: [
{
loader: 'ts-loader',
},
],
exclude: /node_modules/,
},
],
},
plugins: [
new HtmlWebpackPlugin({
template: './src/tpl/index.html',
}),
],
}
webpack.dev.config.js
module.exports = {
//
devtool: 'cheap-module-eval-source-map',
}
webpack.pro.config.js
npm i clean-webpack-plugin -D
const {
CleanWebpackPlugin } = require('clean-webpack-plugin')
module.exports = {
plugins: [new CleanWebpackPlugin()],
}
webpack.config.js
npm i webpack-merge -D # 把两个webpack配置项合并
const merge = require('webpack-merge')
const baseConfig = require('./webpack.base.config')
const devConfig = require('./webpack.dev.config')
const proConfig = require('./webpack.pro.config')
let config = process.NODE_ENV === 'development' ? devConfig : proConfig
module.exports = merge(baseConfig, config)
let hello: string = 'hello world'
document.querySelectorAll('.app')[0].innerHTML = hello
在 ES6 数据类型基础上新增
// 原始类型
let bool: boolean = true
let num: number = 123
let str: string = 'abc'
// 数组
let arr1: number[] = [1, 2, 3]
let arr2: Array<number | string> = [1, 2, 3, '4']
// 元组
let tuple: [number, string] = [0, '1']
// 函数
let add = (x: number, y: number): number => x + y
let compute: (x: number, y: number) => number
compute = (a, b) => a + b
// 对象
let obj: {
x: number; y: number } = {
x: 1, y: 2 }
// symbol
let s1: symbol = Symbol()
// void
let noReturn = () => {
}
// any 可以任意对其修改
let x
x = 1
x = []
undefined 和 null
let un: undefined = undefined
let nu: null = null
/*
解决办法
1.tsconfig 中修改 "strictNullChecks": false
2.使用联合类型 let num: number | undefined | null = 123
*/
num = undefined // 报错
never
// never
let error = () => {
throw new Error('error')
}
let endless = () => {
while (true) {
}
}
枚举:一组有名字的常量集合(可以理解成通讯录)
// 数字枚举(反向映射)
enum Role {
Reporter = 1,
Developer,
Maintainer,
Owner,
Guest,
}
// 字符串枚举
enum Message {
Success = '成功',
Fail = '失败',
}
// 异构枚举
enum Answer {
N,
Y = 'Yes',
}
// 常量枚举(在编译阶段会被移除)作用:不需要一个对象而需要一个对象的值
const enum Month {
Jan,
Feb,
Mar,
}
let month = [Month.Jan, Month.Feb, Month.Mar]
常量枚举
计算枚举
不会在编译阶段进行计算而会保留到程序的执行阶段
// 枚举成员
enum Char {
// constant members
a,
b = Char.a,
c = 1 + 3,
// computed member
d = Math.random(),
e = '123'.length,
}
// 枚举类型(某些情况枚举和枚举成员都可以单独成为一个类型存在)
enum E {
a,
b,
} // 枚举成员没有任何初始值
enum F {
a = 0,
b = 1,
} // 所有成员都是数字枚举
enum G {
a = 'apple',
b = 'banana',
}
// 不同枚举类型之间是不能进行比较的
let e: E = 3
let f: F = 3
let e1: E.a = 1
let e2: E.b
let e3: E.a = 1
let g1: G = G.a
let g2: G.a
ts 核心原则之一是对值所具有的结构进行类型检查,有时被称为"鸭式辨型法"。ts 里,接口的作用就是为这些类型命名和你的代码或第三方代码定义契约
as Result
[x: string]: any
变量使用 const,属性使用 readonly
interface List {
readonly id: number // 只读
name: string
[x: string]: any
age?: number // ?可有可无
}
interface Result {
data: List[]
}
function render(result: Result) {
result.data.forEach(value => {
console.log(value.id, value.name)
if (value.age) {
console.log(value.age)
}
})
}
let result = {
data: [
{
id: 1, name: 'A', sex: 'male' },
{
id: 2, name: 'B' },
],
}
render((<Result>{
data: [
{
id: 1, name: 'A', sex: 'male' },
{
id: 2, name: 'B' },
],
}) as Result)
当你不确定一个接口中有多少个属性时就可以使用可索引的类型
// 数字索引
interface StringArray {
[index: number]: string
}
let chars: StringArray = ['A', 'B']
// 字符串索引
interface Names {
[x: string]: string
[z: number]: string
}
以下两种方式定义是等价的
// 变量声明
let add: (x: number, y: number) => number
// 接口声明
interface Add {
(x: number, y: number): number
}
类型别名
// 使用类型别名
type Add = (x: number, y: number) => number
let add: Add = (a, b) => a + b
混合接口
interface Lib {
(): void
version: string
doSomething(): void
}
function getLib() {
let lib: Lib = (() => {
}) as Lib
lib.version = '1.0'
lib.doSomething = () => {
}
return lib
}
let lib1 = getLib()
lib1()
lib1.doSomething()
函数定义的四种方式
function add1(x: number, y: number) {
return x + y
}
let add2: (x: number, y: number) => number
type add3 = (x: number, y: number) => number
interface add4 {
(x: number, y: number): number
}
// 可选参数必须在必选参数前面
function add5(x: number, y?: number) {
return y ? x + y : x
}
// 函数重载(两个函数名称相同,参数个数/类型不同)
function add6(...rest: number[]): number
function add6(...rest: string[]): string
function add6(...rest: any[]): any {
let first = rest[0]
if (typeof first === 'string') {
return rest.join('')
}
if (typeof first === 'number') {
return rest.reduce((pre, cur) => pre + cur)
}
}
console.log(add6(1, 2, 3)) // 6
console.log(add6('a', 'b', 'c')) // abc
class Dog {
// 要是给constructor加private:既不能被实例化也不能被继承
constructor(name: string) {
this.name = name
}
name: string
run() {
}
private pri() {
}
protected pro() {
}
readonly legs: number = 4
}
console.log(Dog.prototype)
let dog = new Dog('wang')
console.log(dog)
// dog.pri()
// dog.pro()
class Husky extends Dog {
constructor(name: string, public color: string) {
super(name)
this.color = color
// this.pri()
this.pro()
}
// color: string
}
抽象类的好处:
abstract class Animal {
eat() {
console.log('eat')
}
abstract sleep(): void // 抽象方法
}
// let animal = new Animal()
class Dog extends Animal {
constructor(name: string) {
super()
this.name = name
}
name: string
run() {
}
sleep() {
console.log('dog sleep')
}
}
let dog = new Dog('wang')
dog.eat()
class Cat extends Animal {
sleep() {
console.log('cat sleep')
}
}
let cat = new Cat()
let animals: Animal[] = [dog, cat]
animals.forEach(i => {
i.sleep()
})
继承中的多态
class WorkFlow {
step1() {
return this
}
step2() {
return this
}
}
new WorkFlow().step1().step2()
// 继承中的多态:this 既可以是父类型也可以是子类型
class MyFlow extends WorkFlow {
next() {
return this
}
}
new MyFlow().next().step1().next().step2()
注意:
interface Human {
// new (name: string): void
name: string
eat(): void
}
class Asian implements Human {
constructor(name: string) {
this.name = name
}
// private name: string
name: string
eat() {
}
sleep() {
}
}
implements 和 extends 区别
interface Human {
name: string
eat(): void
}
interface Man extends Human {
run(): void
}
interface Child {
cry(): void
}
interface Boy extends Man, Child {
}
let boy: Boy = {
name: '',
run() {
},
eat() {
},
cry() {
},
}
接口除了可以继承接口外还可以继承类,相当于接口把类的成员都抽象了出来(只有类的成员结构而没有具体的实现)
class Auto {
state = 1
}
// 接口中隐含state
interface AutoInterface extends Auto {
}
// Auto的子类也能实现AutoInterface这个接口,C不是Auto的子类,不包含Auto的非公有成员
class C implements AutoInterface {
state = 1
}
// 是Auto的子类,继承了state,接口在抽离类的成员时不仅抽离了公共成员,而且抽离了受保护成员、私有成员
class Bus extends Auto implements AutoInterface {
}
关系:
函数重载:使用相同名称或不同参数数量或类型创建多个方法
联合类型:取值可以为多钟类型中的一个
泛型:不预先确定的数据类型,具体的类型在使用的时候才能确定
// 函数重载
function log(value: string): string
function log(value: string[]): string[] {
return value
}
// 联合类型
function log(value: string | string[]): string | string[] {
return value
}
// 泛型
function log<T>(value: T): T {
console.log(value)
return value
}
// 调用时指定T的类型
log<string[]>(['a', 'b'])
// 使用类型推断
log(['a', 'b'])
不仅可以用泛型定义一个函数,也可以定义一个函数类型
function log<T>(value: T): T {
console.log(value)
return value
}
type Log = <T>(value: T) => T
let myLog: Log = log
泛型接口
function log<T>(value: T): T {
console.log(value)
return value
}
interface Log<T> {
<T>(value: T): T
}
class Log<T> {
run(value: T) {
return value
}
}
let log1 = new Log<number>()
log1.run(1)
let log2 = new Log()
log2.run({
a: 1 })
log2.run('1')
interface Length {
length: number
}
function log<T extends Length>(value: T): T {
console.log(value, value.length)
return value
}
log([1])
log('123')
log({
length: 1 })
TypeScript 编译器在做类型检查时,所秉承的一些原则,以及表现出的一些行为
作用:辅助开发,提高开发效率
**类型推断:**不需要指定变量的类型(函数的返回值类型),TypeScript 可以根据某些规则自动地为其推断出一个类型
let a = 1
let b = [1, null]
let c = (x = 1) => x + 1
window.onkeydown = event => {
// console.log(event.button)
}
interface Foo {
bar: number
}
// let foo = {} as Foo
// foo.bar = 1
// 上面如果把foo.bar注释掉,不会报错,所以建议把对象指定为接口类型(这样就会有提示了)
let foo: Foo = {
bar: 1,
}
// 接口兼容性
interface X {
a: any
b: any
}
interface Y {
a: any
b: any
c: any
}
let x: X = {
a: 1, b: 2 }
let y: Y = {
a: 1, b: 2, c: 3 }
x = y // x能兼容y
// y = x // y不能赋值给x
// 接口参数类型兼容性
interface Point3D {
x: number
y: number
z: number
}
interface Point2D {
x: number
y: number
}
let p3d = (point: Point3D) => {
}
let p2d = (point: Point2D) => {
}
p3d = p2d // p3d能兼容p2d
// p2d = p3d // p3d不能赋值给p2d
当一个类型 Y 可以被赋值给另一个类型 X 时,我们就可以说类型 X 兼容类型 Y
X 兼容 Y: X(目标类型) = Y(源类型)
// 函数兼容性
type Handler = (a: number, b: number) => void
function hof(handler: Handler) {
return handler
}
// 1.参数个数
let handler1 = (a: number) => {
}
hof(handler1)
let handler2 = (a: number, b: number, c: number) => {
}
// hof(handler2)
// 可选参数和剩余参数
let a = (p1: number, p2: number) => {
}
let b = (p1?: number, p2?: number) => {
}
let c = (...args: number[]) => {
}
a = b
a = c
// strictFunctionTypes: false
// b = c
// b = a
c = a
c = b
// 2.参数类型
let handler3 = (a: string) => {
}
// hof(handler3)
// 3.返回值类型
let f = () => ({
name: 'Alice',
})
let g = () => ({
name: 'Alice',
location: 'BeiJing',
})
f = g
// g = f // f返回值类型是g类型的子类型
function overload(a: number, b: number): number
function overload(a: string, b: string): string
function overload(a: any, b: any): any {
}
口诀:
枚举、类、泛型的兼容性:
// 枚举兼容性
enum Fruit {
Apple,
Banana,
}
enum Color {
Red,
Yellow,
}
let fruit: Fruit.Apple = 3
let no: number = Fruit.Apple
// let color: Color.Red = Fruit.Apple
// 类兼容性(和接口相似只比较结构)
class A {
constructor(p: number, q: number) {
}
id: number = 1
private name: string = ''
}
class B {
static s = 1
constructor(p: number) {
}
id: number = 2
private name: string = ''
}
let aa = new A(1, 2)
let bb = new B(1)
// aa = bb
// bb = aa
class C extends A {
}
let cc = new C(1, 2)
aa = cc
cc = aa
// 泛型兼容性
interface Empty<T> {
value: T
}
// let obj1: Empty = {}
// let obj2: Empty = {}
// obj1 = obj2
// obj2 = obj1
let log1 = <T>(x: T): T => {
return x
}
let log2 = <U>(y: U): U => {
return y
}
log1 = log2
TypeScript 能够在特定的区块中保证变量属于某种确定的类型。可以在这个区块中放心地引用次类型的属性,或者调用此类型的方法
enum Type {
Strong,
Week,
}
class Java {
helloJava() {
console.log('Hello Java')
}
java: any
}
class JavaScript {
helloJavaScript() {
console.log('Hello JavaScript')
}
javascript: any
}
function isJava(lang: Java | JavaScript): lang is Java {
return (lang as Java).helloJava !== undefined
}
function getLanguage(type: Type, x: string | number) {
let lang = type === Type.Strong ? new Java() : new JavaScript()
/* if ((lang as Java).helloJava) {
;(lang as Java).helloJava()
} else {
;(lang as JavaScript).helloJavaScript()
} */
// 1.instanceof 判断所属类
/* if (lang instanceof Java) {
lang.helloJava()
} else {
lang.helloJavaScript()
} */
// 2.in 判断是否属于某个对象
/* if ('java' in lang) {
lang.helloJava()
} else {
lang.helloJavaScript()
} */
// 3.typeof 类型保护
/* if (typeof x === 'string') {
x.length
} else {
x.toFixed(2)
} */
// 4. 函数判断
if (isJava(lang)) {
lang.helloJava()
} else {
lang.helloJavaScript()
}
return lang
}
getLanguage(Type.Strong)