TS基础
TS环境
安装ts : npm i -g typescript
编译ts文件: tsc test.ts
类型
类型声明
let a : number
let b : string = '123'
function func(c : boolean) : string {
return c ? '123' : '321'
}
类型介绍
number、string、boolean
字面量
描述变量的值为类型的本身
let d : 'A' | true | 2
d = 'A'
d = true
d = 2
any
let a : any
表示任意类型,一个变量设置类型为any后视为放弃TS类型检测,相当于JS
let a
如果声明变量不指定类型,TS解析器会将该变量视为any
let d : 'A' | true | 2
let e : any
d = e
当变量的类型为any时,赋值给任何类型的变量都不会报错
unknown
类型为unknown的变量,可以给自己赋值任何类型的值,但不能直接赋值给其他变量
let d : 'A' | true | 2
let e : unknown
if(e === 'A'){
d = e
}
let a : string
let b : unknown
if(typeof b === 'string'){
a = b
}
void
用来表示空,一般用在函数返回值,表示没有返回值
function func() : void{
return null
}
function func1() : void{
return undefined
}
没有返回值的函数,默认分配void类型
function warnUser() {
console.log("This is my warning message");
}
never
表示变量某种不可能存在的情况
const a = 123;
if (a !== 123) {
const b = a; // b: never
}
表示函数永远不会返回结果,但是可用来做抛出错误的函数
function func1() : never{
throw new Error('错误')
}
function fun2(): never {
return process.exit(1)
}
function fun3(): never {
while (true) {
}
}
某些时候never可以用来检查代码是否严谨或者是否遗漏
举例:
type All = string | number
function handle(val : All) {
switch (typeof val) {
case 'string':
break;
case 'number':
break;
default:
const a : never = val
break;
}
}
object
let a : object
a = { }
a = function () { }
let a:{name : string,age? : number,sex? : string}
a = {name : '111',sex : '男'}
let a : {name : string , [propName : string] : any}
a = {name : '111',age : 12, true : 1, 1 : '123'}
let d : (a : number, b : number) => number
d = function (n1 : number,n2 : number):number {
return 0
}
array
数组
let a : string[ ]
a = [ 'a' , 'b' , 'c']
let b : Array<number>
b = [1, 2, 3]
let c : (string | number | boolean)[ ];
c = [1 , 1, 'a', true]
tuple 元组
长度、类型、顺序固定的数组
let a: [string, number, boolean]
a = ['1', 1, false]
enum 枚举
列举一些类型
enum MyType {
A = 1,
B,
C
}
let a : MyType
a = MyType.A
enum MyType{
A = 100,
B = 99,
C
}
console.log(MyType.A === MyType.C);//true
类型断言
let a : string
let b : unknown
a = b as string
a = <string>b
- 变量 as 类型
- <类型>变量
类型别名
对于一些复杂的自定义类型,可以用type声明的变量来接收
type mytype = { name : string } & { age : number} & { gender : string}
let a : mytype = {
name : 'zza',
age : 26,
gender : '男'
}
接口
interface myInterface {
name:string,
age:number
}
- 是对行为的抽象,而具体如何行动需要由类(classes)去实现(implement)
- 在TypeScript中,我们使用接口(Interfaces)来定义对象的类型。除了可用于对类的一部分行为进行抽象以外,也常用于对「对象的形状(Shape)」进行描述
接口是为变量或者类制定一个规则或者标准,实现时需要去遵守。
变量声明接口类型时,属性要保持一致
const a : myInterface = {
name:'zza',
age:26
}
类实现接口时,属性实现可以多但不能少
class myClass implements myInterface{
name: string
age: number
gender: string
getInfo():string{
return this.name
}
}
可选属性
interface myInterface {
name:string,
age:number,
gender?:string
}
只读属性
interface myInterface {
name:string,
age:number,
readonly gender:string
}
const 用来修饰变量,readonly 用来修饰属性
任意属性
一般用在对象变量上
interface myInterface {
name:string,
age:number,
[propName: string]: number | string;
}
const a : myInterface = {
name:'zza',
age:26,
addr:'123'
}
用在类中,意义不大
class myClass implements myInterface{
name: string
age: number
[propName: string]: number | string
constructor(name:string,age:number,propName:number | string){
this.name = name
this.age = age
this.propName = propName
}
}
const obj = new myClass('zza',26,12)//myClass {name: "zza", age: 30, propName: 12}
console.log(obj);
可索引属性
interface myInterface {
name:string,
age:number,
[propName: number]: number
}
const a : myInterface = {
name:'zza',
age:26,
123:0
}
接口继承
extends
interface myInterface {
name:string,
age:number,
}
interface myInterface2 extends myInterface{
addr:string
}
class myClass {
phone:string
emil:string
}
interface myInterface3 extends myClass{
call():void;
}
类型别名和接口的区别
- 写法区别
type A = number;
type B = A | string | 'A'
interface A {
name: string
}
合并写法
type A = {
name:string,
age:number
}
type B = A & {
gender:string
}
interface A {
name:string,
age:number
}
interface B extends A {
gender:string
}
- 重复声明
类型别名重复声明会报错
interface A {
name:string,
age:number
}
interface A {
gender:string
}
类
class A {
name : string
constructor(name:string){
this.name = name
}
getInfo(){
console.log(this.name);
}
}
class B extends A {
age:number
constructor(name:string, age:number){
super(name)
this.age = age
}
getInfo() {
super.getInfo()
console.log(this.age);
}
}
const a = new B('zza',26)
console.log(a);
a.getInfo()
readonly 修饰符
- 只读属性必须在声明时或构造函数里被初始化。
class Person {
readonly name: string = 'abc'
constructor(name: string) {
this.name = name
}
}
let john = new Person('John')
公有、私有、受保护的修饰符
public 公有
属性和方法默认为公有,自己、类外和子类均可调用
private 私有
属性和方法只能在自己类中调用
protected 受保护的
属性和方法可以在自己和子类中调用
class Person {
protected name: string;
constructor(name: string) {
this.name = name;
}
protected getName():string{
return this.name
}
}
class Employee extends Person {
private department: string;
constructor(name: string, department: string) {
super(name);
this.department = department;
}
getWorkInfo() {
return `我叫${this.getName()},我工作在${this.department}`;
}
}
let aEmployee = new Employee('durban', '华盛顿');
console.log(aEmployee.getWorkInfo());
注意:readonly 与 上述修饰符不冲突,可以联合使用
存取器
class A{
private _name:string
constructor(name:string){
this._name = name
}
get name(){
return this._name
}
set name(value:string){
this._name = value
}
}
静态成员
- 用static修饰的属性或方法
- 存在于类本身而不是类的实例上
class Person {
name1: string = 'A'
static name2: string = 'B'
}
console.log(Person.name2)
console.log(new Person().name1)
- 在静态方法中可以调用静态的属性和方法,也可以调用this(可以将非静态的属性暴露),但不能用this获取非静态方法
class A {
private static age:string = '26'
private name:string = 'zza'
getInfo(){
return this
}
static getStaticInfo(){
return this
}
}
console.log(A.getStaticInfo());
console.log(new A().getInfo());
- 类中有个内置的只读属性name,属于静态属性,在类中可以再声明一个name属性但不能修饰为静态属性。
抽象类
- 用abstract修饰的类是抽象类
- 抽象类不能用来实例化对象,只能作为父类供子类来继承
- 抽象类中可以添加普通属性和方法,也可以添加抽象属性和方法
- 被抽象的属性和方法不允许有值或实现,也不允许被constructor赋值
- 如果继承抽象类的子类不是抽象类,就必须将其中所有的抽象属性和方法具体化
abstract class Animal {
abstract name:string
constructor() {
}
abstract say():void;
run():void{
console.log('跑');
}
}
abstract class sAnimal extends Animal{
abstract age:number
}
class Dog extends sAnimal {
name: string;
age: number;
run(): void {
super.run()
}
say(): void{
console.log('叫');
}
}
多态
abstract class Animal {
name: string;
constructor(name: string) {
this.name = name;
}
abstract eat(): any;
run() {
console.log('动物跑');
}
}
class Cat extends Animal {
eat() {
return "吃鱼";
}
}
class Dog extends Animal {
eat() {
return "吃肉";
}
// run() {
// console.log('狗跑');
// }
}
var cat: Animal = new Cat("猫");
var dog: Animal = new Dog("狗");
let fun: (x: Animal) => void = (x: Animal) => {
console.log(x.name + x.eat());
}
fun(cat)
fun(dog)
cat.run()
dog.run()
接口和抽象类的区别
区别:
- 接口使用implemets实现,抽象类使用extends继承
interface A {
name : string
}
class B implements A {
name: string //必须
age : string
}
abstract class A {
abstract name : string
}
class B extends A {
name: string //必须
age : number
}
- 接口可以实现多个,以","分开,而继承只能有一个
interface A1 {
name : string
}
interface A2 {
age : number
}
interface A3 {
gender : string
}
class B implements A1,A2,A3 {
name: string //必须
age : number //必须
gender: string //必须
}
- 抽象类中,可以有抽象方法及非抽象方法,接口中只能有抽象方法
abstract class A {
abstract name : string
addr:string
}
class B extends A {
name: string //必须
addr:string //非必须
age : number
}
- 抽象类可以继承抽象类也可以实现接口,而接口只可以继承接口和类,不可以实现接口和类
共同点:
- 都有抽象方法,都需要被实现
- 都不能实例化
- 抽象类可以被继承,接口也能被继承
函数
js:
// 命名函数
function fun1(x, y) {
return x + y
}
// 匿名函数
let fun2 = function(x, y) {
return x + y;
}
ts中增加类型声明:
function fun1(x: number, y: number): number {
return x + y
}
let fun2 = function(x: number, y: number): number {
return x + y
}
对于给变量指定类型赋值的完整写法
let myAdd2: (x: number, y: number) => number =
function(x: number, y: number): number {
return x + y
}
可选参数和默认参数
function fun(a: string='A', b?: string): string {
if (b) {
return a + '-' + b
} else {
return a
}
}
console.log(fun('1', '2'))
console.log(fun('3'))
console.log(fun())
剩余参数
function letters(x: string, ...args: string[]) {
console.log(x, args)
}
letters('abc', 'c', 'b', 'a')
函数重载
// 重载函数声明
function add (x: string): string
function add (x: number): number
// 定义函数实现
function add(x: string | number): string | number {
if (typeof x === 'string') {
return x
} else if (typeof x === 'number') {
return x
}
}
const a : number = add(1)
const b : string = add('1')
console.log(a)
console.log(b)
泛型
- 指在定义函数、接口或类的时候,不预先指定具体的类型,而在使用的时候再指定具体类型的一种特性。
函数泛型
function fun<T,K>(a:T,b:K):T{
console.log(b);
return a
}
fun<string,number>('123',123)
实际使用:
function getNumberArr(value:number,count:number):number[] {
const a : number[] = []
for(let i = 0;i < count;i++){
a.push(value)
}
return a
}
function getStringArr(value:string,count:number):string[] {
const a : string[] = []
for(let i = 0;i < count;i++){
a.push(value)
}
return a
}
const arr = getNumberArr(123,10)
console.log(arr[0].toFixed());
const arr2 = getStringArr('123',10)
console.log(arr2[0].length);
利用泛型:
function getArr<T>(value:T,count:number):T[] {
const a : T[] = []
for(let i = 0;i < count;i++){
a.push(value)
}
return a
}
const arr3 = getArr<string>('123',10)
const arr4 = getArr<number>(123,10)
console.log(arr3[0].length);
console.log(arr4[0].toFixed());
泛型接口
- 在定义接口时,为接口中的属性和方法定义泛型类型,在使用接口时,再指定具体的泛型类型
interface A <T> {
arr:T[]
value:T
}
class B implements A<number>{
arr:number[]
value: number
}
使用例子:
interface IbaseCRUD <T> {
data: T[]
add: (t: T) => void
getById: (id: number) => T
}
class User {
id?: number; //id主键自增
name: string; //姓名
age: number; //年龄
constructor (name, age) {
this.name = name
this.age = age
}
}
class UserCRUD implements IbaseCRUD <User> {
data: User[] = []
add(user: User): void {
user = {...user, id: Date.now()}
this.data.push(user)
console.log('保存user', user.id)
}
getById(id: number): User {
return this.data.find(item => item.id===id)
}
}
const userCRUD = new UserCRUD()
userCRUD.add(new User('tom', 12))
userCRUD.add(new User('tom2', 13))
console.log(userCRUD.data)
泛型类
- 在定义类时, 为类中的属性或方法定义泛型类型 在创建类的实例时, 再指定特定的泛型类型
class A <T> {
default:T
add:(x:T,y:T) => T
}
const a = new A<number>()
a.default = 1
a.add = (x:number,y:number) =>{
return x + y
}
const b = new A<string>()
b.default = '1'
b.add = (x:string,y:string) =>{
return x + y
}
泛型约束
function fn <T>(x: T): void {
console.log(x.length) // 报错
}
这时需要一个规则来约束这个泛型T ,让他可以使用length属性
interface B {
length:number
}
function fn <T extends B>(x: T): void {
console.log(x.length) // 报错
}
注意:泛型约束中的extends意义上和实现相同,指T必须拥有接口B中的属性和方法
interface B {
length:number,
add:(a:number,b:number)=>number
}
function fn <T extends B>(x: T): void {
console.log(x.length)
console.log(x.add(100,200));
}
class C implements B{
length: number
add: (a: number, b: number) => number
}
fn<C>(
{
length:2,
add:(x,y)=>{
return x+y
}
}
)
泛型工具
keyof
interface Foo {
name: string;
age: number
}
type T = keyof Foo // -> "name" | "age"
const a : T = "name"
const b : T = "age"
type A = keyof any // type A = string | number | symbol
in
type Keys = "a" | "b"
type Obj = {
[p in Keys]: any
} // -> { a: any, b: any }
const a : Obj ={
a:'123',
b:123
}
infer
- infer 声明一个类型变量并且对它进行使用,仅条件类型的 “extends” 子句中才允许 “infer” 声明。
type A<T> = T extends (x: any) => infer P ? P : T;
type B = (x:number)=>number
const a : A<string> = '123'
const b : A<B> = 2
Partial 作用是将传入的属性变为可选项
type myPartial<T> = { [P in keyof T]?: T[P] };
interface A {
a:string
b:string
}
const a: myPartial<A> = {
a:'132',
// b:'123'
}
Required 作用是将传入的属性变为必选项
type myRequired<T> = { [P in keyof T]-?: T[P] }
interface A {
name:string
age:number
addr?:string
}
const a : myRequired<A> ={
name:'zza',
age:26,
addr:'qewr'
}
Record 后面的泛型就是对象键和值的类型
type myRecord<K extends keyof any, T> = { [P in K]: T };
const a : myRecord<string,number> = {
// name:'zza', 报错
age:26
}
Pick 从 T 中取出 一系列 K 的属性
type myPick<T, K extends keyof T> = { [P in K]: T[P] };
interface A {
name:string,
age:number,
addr:string
}
type B = 'name' | 'age'
const a : myPick<A,B> = {
name:'zza',
age:26
}
ReturnType 获取返回值类型
type myReturnType<T> = T extends (...args: any[]) => infer R ? R: T;
type B = (x:number)=>number
const a : myReturnType<string> = '123'
const b : myReturnType<B> = 2