强类型和弱类型
- js属于弱类型语言
- ts将js这种弱类型语言变成强类型语言
静态类型和动态类型
- 静态类型语言:在编译阶段确定所有变量类型
- 动态类型语言:在执行阶段确定所有变量类型
ts入门
- 1.全局安装ts:
npm i typescript -g(可使用tsc) - 2.ts配置文件初始化:
tsc --init生成tsconfig.json - 3.编译ts文件:
tsc ./src/index - 4.添加webpack打包工具
- 5.添加ts-loader
ts基本类型
- 原始类型
let bool: boolean = true
let num: number | undefined | null = 123
let str: string = 'abc'
// str = 123
// 数组
let arr1: number[] = [1, 2, 3]
let arr2: Array<number | string> = [1, 2, 3, '4']
// 元组
let tuple: [number, string] = [0, '1']
// tuple.push(2) 可以添加新元素
// console.log(tuple)
// tuple[2] 不能访问到新元素
// 函数
let add = (x: number, y: number) => x + y //返回值类型可以不写,ts有类型推断
let compute: (x: number, y: number) => number
compute = (a, b) => a + b
// 对象
let obj: { x: number, y: number } = { x: 1, y: 2 }
obj.x = 3
// symbol
let s1: symbol = Symbol()
let s2 = Symbol()
// console.log(s1 === s2) false
// undefined, null
let un: undefined = undefined
let nu: null = null
num = undefined //定义num时候就要允许类型为undefined
num = null
// void 没有任何返回值
let noReturn = () => {}
// 如果不指定类型,默认any类型,和js没有区别
let x
x = 1
x = []
x = () => {}
// never
let error = () => {
throw new Error('error')
}
let endless = () => {
while(true) {}
}
- 枚举类型
一组有名字的常量集合,通常用于定义一些常量,便于代码阅读,枚举分为数字枚举和字符串枚举
- 如下代码,可读性非常差,可维护性也差
可以使用枚举来解决
enum Role {
Reporter = 1, //自定义初始值
Developer,
Maintainer,
Owner,
Guest
}
// 数字枚举,值会递增
enum Role {
Reporter = 1, //自定义初始值
Developer,
Maintainer,
Owner,
Guest
}
// console.log(Role.Reporter)
// console.log(Role)
// 字符串枚举
enum Message {
Success = '恭喜你,成功了',
Fail = '抱歉,失败了'
}
// 异构枚举,容易混淆,不建议使用
enum Answer {
N,
Y = 'Yes'
}
// 枚举成员,只读,不能修改
// Role.Reporter = 0
enum Char {
// const member
a,
b = Char.a,
c = 1 + 3,
// computed member
d = Math.random(),
e = '123'.length,
f = 4
}
// 常量枚举 用const声明,在编译阶段会被移除
const enum Month {
Jan,
Feb,
Mar,
Apr = Month.Mar + 1,
// May = () => 5
}
let month = [Month.Jan, Month.Feb, Month.Mar]
// 枚举类型
enum E { a, b }
enum F { a = 0, b = 1 }
enum G { a = 'apple', b = 'banana' }
let e: E = 3
let f: F = 3
// console.log(e === f) 不能进行比较
let e1: E.a = 3
let e2: E.b = 3
let e3: E.a = 3
// console.log(e1 === e2)
// console.log(e1 === e3) //只有自己可以和自己比较
let g1: G = G.a
let g2: G.a = G.a
ts对象类型接口
interface StringArray {
[index: number]: string
}
let chars: StringArray = ['a', 'b']
type Add = (x: number, y: number) => number
let add: Add = (a: number, b: number) => a + b
ts函数类型接口
- 函数定义固定参数
// 函数定义
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
}
add5(1)
- 函数重载
function add8(...rest: number[]): number;
function add8(...rest: string[]): string;
function add8(...rest: any[]) {
let first = rest[0];
if (typeof first === 'number') {
return rest.reduce((pre, cur) => pre + cur);
}
if (typeof first === 'string') {
return rest.join('');
}
}
console.log(add8(1, 2))
console.log(add8('a', 'b', 'c'))
ts函数类型接口
- 类的继承和成员修饰符,抽象类
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
this.pri()
}
public name: string = 'dog'
run() {}
private pri() {} //私有方法,只能自己调用
protected pro() {} //保护方法只能自己和子类使用
readonly legs: number = 4 //只读
static food: string = 'bones' //静态成员,只能通过类名,不能通过子类调用,但是可以被继承
sleep() {
console.log('Dog sleep')
}
}
// console.log(Dog.prototype)
let dog = new Dog('wangwang')
// console.log(dog)
// dog.pri()
// dog.pro()
console.log(Dog.food) //只能通过类名
//console.log(dog.food)//报错,
dog.eat()
class Husky extends Dog {
//实例属性
constructor(name: string, public color: string) {
super(name)
this.color = color
// this.pri()
this.pro()
}
// color: string
}
console.log(Husky.food) //继承后可以调用
- 抽象方法的多态,继上一个
class Cat extends Animal {
sleep() {
console.log('Cat sleep')
}
}
let cat = new Cat()
let animals: Animal[] = [dog, cat]
animals.forEach(i => {
i.sleep()
})
ts泛型
function log<T>(value: T): T {
console.log(value);
return value;
}
log<string[]>(['a', ',b', 'c'])
log(['a', ',b', 'c'])
// type Log = <T>(value: T) => T
// let myLog: Log = log
// interface Log<T> {
// (value: T): T
// }
// let myLog: Log<number> = log
// myLog(1)
class Log<T> {
run(value: T) {
console.log(value)
return value
}
}
let log1 = new Log<number>()
log1.run(1)
let log2 = new Log()
log2.run({ a: 1 })
interface Length {
length: number
}
function logAdvance<T extends Length>(value: T): T {
console.log(value, value.length);
return value;
}
logAdvance([1])
logAdvance('123')
logAdvance({ length: 3 })
1.函数和类可以轻松地支持多种类型,增强程序的课拓展性 2.不必写多条函数重载,冗长的联合类型声明,增强代码可读性 3.灵活控制类型之间的约束
ts兼容性
x兼容Y:x(目标类型)=Y(源类型) 口诀: 结构之间兼容:成员少的兼容成员多的 函数之间兼容:参数多的兼容参数少的
ts类型保护机制
//instanceof
if (lang instanceof Java) {
lang.helloJava()
// lang.helloJavaScript()
} else {
lang.helloJavaScript()
}
// in
if ('java' in lang) {
lang.helloJava()
} else {
lang.helloJavaScript()
}
// typeof
if (typeof x === 'string') {
console.log(x.length)
} else {
console.log(x.toFixed(2))
}
ts高级类型
- 交叉类型和联合类型
interface DogInterface {
run(): void
}
interface CatInterface {
jump(): void
}
let pet: DogInterface & CatInterface = {
run() {},
jump() {}
}
class Dog implements DogInterface {
run() {}
eat() {}
}
class Cat implements CatInterface {
jump() {}
eat() {}
}
enum Master { Boy, Girl }
function getPet(master: Master) {
let pet = master === Master.Boy ? new Dog() : new Cat();
// pet.run()
//pet.jump()
pet.eat()
return pet
}
interface Square {
kind: "square";
size: number;
}
interface Rectangle {
kind: "rectangle";
width: number;
height: number;
}
interface Circle {
kind: "circle";
radius: number;
}
type Shape = Square | Rectangle | Circle
function area(s: Shape) {
switch (s.kind) {
case "square":
return s.size * s.size;
case "rectangle":
return s.height * s.width;
case 'circle':
return Math.PI * s.radius ** 2
default:
return ((e: never) => {throw new Error(e)})(s)
}
}
console.log(area({kind: 'circle', radius: 1}))
- 索引类型
let obj = {
a: 1,
b: 2,
c: 3
}
// function getValues(obj: any, keys: string[]) {
// return keys.map(key => obj[key])
// }
function getValues<T, K extends keyof T>(obj: T, keys: K[]): T[K][] {
return keys.map(key => obj[key])
}
console.log(getValues(obj, ['a', 'b']))
// console.log(getValues(obj, ['d', 'e']))
// keyof T 约定key的联合类型
interface Obj {
a: number;
b: string;
}
let key: keyof Obj
// T[K] //约定取值
let value: Obj['a']
// T extends U
- 映射类型
interface Obj {
a: string;
b: number;
c: boolean;
}
type ReadonlyObj = Readonly<Obj> //只能映射
type PartialObj = Partial<Obj> //可选映射
type PickObj = Pick<Obj, 'a' | 'b'> //抽取映射
type RecordObj = Record<'x' | 'y', Obj> //非同态映射
- 条件类型
type TypeName<T> =
T extends string ? "string" :
T extends number ? "number" :
T extends boolean ? "boolean" :
T extends undefined ? "undefined" :
T extends Function ? "function" :
"object";
type T1 = TypeName<string>
type T2 = TypeName<string[]>
// (A | B) extends U ? X : Y
// (A extends U ? X : Y) | (B extends U ? X : Y)
type T3 = TypeName<string | string[]>
type Diff<T, U> = T extends U ? never : T
type T4 = Diff<"a" | "b" | "c", "a" | "e">
// Diff<"a", "a" | "e"> | Diff<"b", "a" | "e"> | Diff<"c", "a" | "e">
// never | "b" | "c"
// "b" | "c"
type NotNull<T> = Diff<T, null | undefined>
type T5 = NotNull<string | number | undefined | null>
// Exclude<T, U>
// NonNullable<T>
// Extract<T, U>
type T6 = Extract<"a" | "b" | "c", "a" | "e">
// ReturnType<T>
type T8 = ReturnType<() => string>
ts的命名空间
namespace Shape {
export function square(x: number) {
return x * x
}
}
console.log(Shape.square(2))
ts的声明合并
- 接口合并
interface A {
x: number;
// y: string;
foo(bar: number): number; // 5
foo(bar: 'a'): string; // 2
}
interface A {
y: number;
foo(bar: string): string; // 3
foo(bar: string[]): string[]; // 4
foo(bar: 'b'): string; // 1
}
let a: A = { //具备两个接口所有的参数
x: 1,
y: 2,
foo(bar: any) {
return bar
}
}
- 命名空间合并
class C {}
namespace C { //命名空间的声明必须在类之后
export let state = 1
}
console.log(C.state)
ts中如何编写声明文件
- 安装第三方类库的时候,我们需要对其进行定义,比如jquery这个库,这个时候我们可以使用社区提供的
npm i @types/jquery -D
- 如果社区没有提供,我们可以怎么定义呢?
//js
function globalLib(options) {
console.log(options);
}
globalLib.version = '1.0.0';
globalLib.doSomething = function() {
console.log('globalLib do something');
};
//d.ts文件
declare function globalLib(options: globalLib.Options): void;
declare namespace globalLib {
const version: string;
function doSomething(): void;
interface Options {
[key: string]: any
}
}