Write and share what I see and hear
点击访问本文代码地址
yarn init -y
yarn add typescript --dev
// 01-getting-started.ts
const hello = (name: string) => {
console.log(`Hello ,${name}`);
}
hello('TypeScript')
yarn tsc 01-getting-started.ts
yarn tsc -init
生成配置文件 tsconfig.json {
"compilerOptions":{
"target": "es5", // 编译后的es版本 eg: es5 es2015
"module": "commonjs", // 使用模块规范
"rootDir": "src", // 编译入口文件夹
"sourceMap": true, // 是否开启sourceMap
"outDir": "dist", // 编译后的出口文件夹
"strict": true, // 是否开启严格模式,开启后需要为每个变量设置类型, any 类型也需要添加
}
}
yarn tsc
注意:配置文件 tsconfig.json 只对指定入口文件夹下的文件进行编译
const a: string = 'foobar'
const b: number = 100 // 包括 NaN Infinity
const c: boolean = true // false
// const d: string = null
TypeScript 中以上三种类型在非严格模式下默认允许为空,即可以赋值为 null undefined
const e: void = undefined // 通值,一般在函数没有返回值的时候标记函数的类型,只能存放 null undefined,严格模式下只能是 undefined
const f: null = null
const g: undefined = undefined
ES2015中新增的 symbol 类型的值,直接使用会提示错误
解决方案
使用方案 2 时, console 会报错,原理同 symbol 报错一样,需要在lib中引入 BOM 和 DOM 标准库----“DOM”,“DOM” 包含 BOM 和 DOM
const h: symbol = Symbol()
--locale zh-CN
不推荐使用中文错误,不利于搜索问题
(function () {
const a: number = 123
})()
export {}
export 只是 ESModule 的语法,并不是导出了一个空对象
TypeScript 中的 Object 并不单指普通的对象类型,泛指所有的非原始类型,对象、数组、函数
export {} // 确保与其他示例没有冲突
const foo: object = function () {} // [] // {}
如果只要普通对象类型,需要使用类似字面量语法,标记类型
const obj: { foo: number } = { foo: 123 }
标记多个类型,可以用逗号分隔
这里要求赋值的类型结构必须与定义的类型结构完全一致,不能多或少
const obj2: { foo: number, bar: string } = { foo: 123, bar: 'string' }
限制对象应该使用接口
定义数组的方式
const arr1: Array<number> = [1, 2, 3]
const arr2: number[] = [1, 2, 3]
示例
function sum (...args: number[]) {
// 判断是不是每个成员都是数字
return args.reduce((prev, current) => prev + current, 0)
}
// 调用时使用非数字数组会报错
sum(1, 2 , 3, 'foo')
元组类型是一种特殊的数据结构
元组其实是一个明确元素数量及元素类型的数组
const tuple: [number, string] = [1, 'string']
访问元组中的某个元素
const age = tuple[0]
const name = tuple[1]
const [age, name] = tuple
- 元组一般用在一个函数中返回多个返回值,这种类型现在越来越常见
- react 中 useState 的 HOOK函数中返回的就是一个元组
- es2017 中 Object.entries 方法,获取一个对象中所有的键值数组,得到的每一个键值,就是一个元组,因为它是一个固定长度的
枚举(Enum)
const PostStatus = {
Draft: 0,
Unpublished: 1,
Published: 2
}
const post = {
title: 'title',
content: 'content',
status: PostStatus.Draft
}
enum PostStatus {
Draft = 0,
Unpublished = 1,
Published = 2
}
const post = {
title: 'title',
content: 'content',
status: PostStatus.Draft
}
枚举类型的值可以不用 = 指定,如果不指定默认从0累加,如果给第一个设置了值,后面的依次累加
除了可以设置数字,还可以是字符串,字符串无法自增长,所以字符串需要手动为每个类型赋值
PostStatus[0] // Draft
const enum PostStatus {
Draft = 0,
Unpublished = 1,
Published = 2
}
函数的类型约束,即是指对函数的输入输出进行类型限制
函数声明
function func1 (a: number, b: number): string {
return ''
}
func1(100, 200)
function func1 (a: number, b?: number): string {
return ''
}
function func2 (a: number, b: number = 10): string {
return ''
}
无论是可选参数,还是默认参数,都需要出现在参数列表的最后,因为参数会按照位置进行传递
function func2 (a: number, b: number = 10, ...rest): string {
return ''
}
函数表达式
const func2 = function func2 (a: number, b: number): string {
return ''
}
function stringify (value: any) {
return JSON.stringify(value)
}
stringify('string')
stringify(100)
stringify(true)
let foo: any = 'string'
foo = 100
foo.bar()
在 TypeScript 中,如果我们没有通过类型注解明确标记一个变量的类型,TypeScript 会根据这个变量的使用情况,推断这个变量类型,这个特性叫做隐式类型推断
let age = 18 // 此时 age 被推断为 number 类型
age = 'string' // 报错
let foo // 声明变量未赋值,会被标记为 any 类型
foo = 100
foo = 'string'
虽然 TypeScript 支持隐式类型推断,而且这种隐式类型推断可以简化一部分代码,这里仍然建议为每个变量尽可能的添加明确的类型,便于后期更直观的理解代码
// 假定这个 nums 辣子一个明确的接口
let nums = [110, 120, 130, 112]
// 此时调用 find 方法获取第一个大于0的数字是可以获取到的,
const res = nums.find(i => i > 0)
const square = res * res // 报错
const num = res as number
const num = <number>res
如果在代码中使用 jsx,这里的尖括号会和 jsx 中的标签产生冲突,在 jsx 下不能使用这种断言方式
类型断言并不是类型转换,编译过后这个断言就不会存在了
// 此函数入参隐性要求必须要有 title, content 属性
// 可以使用接口表现这种约束
function printPost (post) {
console.log(post.title);
console.log(post.content);
}
interface Post {
title: string // 可以使用 , 分隔,更标准的语法是 ; 分隔,这个分号也可以省略
content: string
}
// 此函数入参隐性要求必须要有 title, content 属性
// 可以使用接口表现这种约束
function printPost (post: Post) {
console.log(post.title);
console.log(post.content);
}
printPost({
title: 'title',
content: 'content'
})
TypeScript 中的接口是为了给有结构的数据做约束的,在实际运行阶段,这种接口并没有意义,编译后不会体现
可选成员
interface Post {
title: string // 可以使用 , 分隔,更标准的语法是 ; 分隔,这个分号也可以省略
content: string
subtitle?: string // 可选成员 相当于给 subtitle 标记类型为 string 或 undefined
}
只读成员
interface Post {
title: string // 可以使用 , 分隔,更标准的语法是 ; 分隔,这个分号也可以省略
content: string
subtitle?: string // 可选成员 相当于给 subtitle 标记类型为 string 或 undefined
readonly summary: string // 只读成员
}
printPost({
title: 'title',
content: 'content',
summary: 'summary'
})
动态成员
interface Cache1 {
[prop: string]: string
}
const cache: Cache1 = {}
cache.foo = 'value1'
cache.bar = 'value2'
类,描述一类具体事物/对象的抽象特征
JavaScript
TypeScript
类的属性在使用前必须要声明,为了给属性做类型标注
class Person {
name: string // = 'init' // 指定类的属性,可以使用 = 赋初始值
age: number
// TypeScript 中类的属性必须要有一个初始值,可以使用 = 指定,也可以在构造函数 constructor 中初始化,否则会报错
constructor (name: string, age: number) {
this.name = name
this.age = age
}
sayHi (msg: string): void {
console.log(`I am ${this.name}, ${msg}`);
}
}
public 公有成员 默认 有无 public 均可,建议添加
private 私有属性 属性前添加 private 表示私有属性,只能内部访问]
protected 受保护的属性 属性前添加 protected 表示受保护的属性,只能内部访问]
class Person {
public name: string
private age: number
protected gender: boolean
constructor (name: string, age: number) {
this.name = name
this.age = age
this.gender = true
}
sayHi (msg: string): void {
console.log(`I am ${this.name}, ${msg}`);
console.log(this.age);
}
}
const tom = new Person('tom', 18)
console.log(tom.name)
// console.log(tom.age) // 报错
// console.log(tom.gender) // 报错
private 与 protected 的区别
class Student extends Person {
constructor (name: string, age: number) {
super(name, age)
console.log(this.gender);
}
}
构造函数 constructor 的访问修饰符默认为 public
如果设置为 private,那这个类型就不能在外部被实例化,也不能被继承,此时,只能这个类的内部创建静态方法,在静态方法中创建这个类的实例
class Student extends Person {
private constructor (name: string, age: number) {
super(name, age)
console.log(this.gender);
}
static create (name: string, age: number) {
return new Student(name, age)
}
}
const jack = Student.create('jack', 18)
如果设置为 protected,那这个类型就不能在外部被实例化,但可以被继承
class Person {
public name: string
private age: number
protected readonly gender: boolean
constructor (name: string, age: number) {
this.name = name
this.age = age
this.gender = true
}
sayHi (msg: string): void {
console.log(`I am ${this.name}, ${msg}`);
console.log(this.age);
}
}
const tom = new Person('tom', 18)
console.log(tom.name)
tom.gender = false // 报错
不同的类之间可能会有共同的特征,这些不同的类的共同特征可以使用接口进行抽象
例如: 手机和座机 都能打电话,可以看作它们拥有相同的协议(接口)
// 此处 人和动物 都有 吃和跑 的方法,但是 人与动物 的方法是不同的,只是都有这个能力,但能力的实现是不同的
class Person {
eat (food: string): void {
console.log(`Person eat: ${food}`);
}
run (distance: number): void {
console.log(`Person run: ${distance}`);
}
}
class Animal {
eat (food: string): void {
console.log(`Animal eat: ${food}`);
}
run (distance: number): void {
console.log(`Animal run: ${distance}`);
}
}
interface EatAndRun {
eat (food: string): void
run (distance: number): void
}
class Person implements EatAndRun {
eat (food: string): void {
console.log(`Person eat: ${food}`);
}
run (distance: number): void {
console.log(`Person run: ${distance}`);
}
}
class Animal implements EatAndRun {
eat (food: string): void {
console.log(`Animal eat: ${food}`);
}
run (distance: number): void {
console.log(`Animal run: ${distance}`);
}
}
interface Eat {
eat (food: string): void
}
interface Run {
run (distance: number): void
}
class Person implements Eat, Run {
eat (food: string): void {
console.log(`Person eat: ${food}`);
}
run (distance: number): void {
console.log(`Person run: ${distance}`);
}
}
class Animal implements Eat, Run {
eat (food: string): void {
console.log(`Animal eat: ${food}`);
}
run (distance: number): void {
console.log(`Animal run: ${distance}`);
}
}
// 在 class 前添加 abstract 定义抽象类
abstract class Animal {
eat (food: string): void {
console.log(`Animal eat: ${food}`);
}
// 抽象类中还可以定义抽象方法,抽象方法也不需要返回体
abstract run (distance: number): void
}
// 抽象类只能被继承,不能使用 new 的方式创建实例对象,抽象类中有抽象方法,子类中就必须要实现这个抽象方法
class Dog extends Animal {
run(distance: number): void {
console.log(`Dog run: ${distance}`);
}
}
const d = new Dog()
d.eat('food')
d.run(100)
泛型是指在定义函数接口类的时候没有指定具体类型,等到使用时再定义类型,这样一种特征
以函数为例,泛型就是在声明函数时不指定具体类型,而是调用时传递具体的类型,这样可以极大程度的服用代码
// 生成指定长度的数组
function createNumberArray(length: number, value: number): number[] {
// Array 其实是一个 any 类型,可以在使用时指定需要的类型
const arr = Array<number>(length).fill(value)
return arr
}
const res = createNumberArray(3, 100)
// => res => [100, 100, 100]
function createArray<T> (length: number, value: T): T[] {
const arr = Array<T>(length).fill(value)
return arr
}
const res = createArray<string>(3, '100')
// => res => [100, 100, 100]
import { cameCase } from 'loadsh'
declare function cameCase(input:string): string
const res = cameCase('hello typed')
yarn add &types/lodash --dev