为什么需要TS呢?
- 使用ts的时候在写代码的时候机会抛出错误,并会抛出相关错误。
- 编写代码会提示相关参数的提示。
- 对代码的声明说明,会更好理解代码。
viod和undefined、null的区别?
viod代表变量为undefined或者null。
viod不可以将值赋值给其他变量,而undefined和null可以。
any和unknown有什么区别?
unknown类型比any类型更安全。unknown类型引用他的属性和方法时,ts会报错,同时只能赋值unknown和any对象,复制给其他对象会报错。
什么是接口interface?
interface定义一个对象里面所有的属性及属性类型。如果两个interface名称一样会合并。
可选式操作符?:则表明此属性可出现可不出现。
任意属性[propName:String]:any允许添加新的任意属性。需要注意的是一旦定义了任意属性,那么确定属性和可选属性的类型都必须是它的类型的子集。如下定义就会报错:
interface Person {
name:string,
age?:number,//会报错类型“number”不能赋给“string”索引类型的“string”
[propName: string]: string
}
联合类型|可扩展类型。
交叉类型 & 满足两个或多个接口的交叉类型。
类型断言as:
let fn = function(num:number | string):void {
console.log((num as string).length)//只有断言正确才会输出其length
//或者
console.log((<String>num).length)
}
fn('123')
readonly只读类型,不能赋值
interface Person {
readonly name:string,
age?:number,
[propName: string]: any
}
interface定义函数方式
interface Fn {
cb():number//number代表返回值的类型
}
let f:Fn = {
cb: ():number => {
return 123
}
}
组合式interface
interface A{
name:string
}
interface B extends A {
age:number
}
let p:B = {
name:'张三',
age:12
}
数组声明
数组声明的时候如果指定一个类型,则有其他类型会报错。
var arr:number[] = [1,2,3]//数字类型的数组
var arr2:string[] = ["1","2","3"]//数字类型的数组
var arr3:any[] = [1,'2',true]//任意类型的数组
//或者
let arr:Array<number> = [1,2,3,4]
多维数组
let arr:number[][][] = [[[]]]
let arr:Array<Array<number | string>> = [[1,2,3,'124']]
类数组
function foo(...arg:any):void {
let arr:IArguments = arguments
}
foo(1,2,3)
//IArguments相当于
interface IArguments {
[index: number]: any;
length: number;
callee:Function;
}
接口描述数组
// 接口描述数组
interface ArrNumber {
[index:number] : string
}
let arr: ArrNumber = ['1','2','3']
函数重载
如下方式:
function fn(p1:number):void
function fn(p1:string, p2:number):void
function fn(p1:any,p2?:any):void {
console.log(p1,p2);
}
fn(1)
fn('123',233)
在执行函数的上方可以定义多个规则,会根据调用的方式选择不同的规则。执行函数的定义要包含所有的定义规则。
类的修饰符
public 内部外部都能访问,默认是public
private 是私有属性,只能内部访问
protected是私有属性,内部和子类能访问
静态属性 static,可直接访问,静态属性里面不能this访问除静态属性其他的值,同时内部属性除去静态属性this能访问它,其他属性要通过类名来访问。
类的参数要在constructor上面定义。
class Person {
public name:string
public age:number
public sub:boolean//声明了必须要用
private dd: string = '2'
static aa:number = 200
constructor(name:string,age:number,sub:boolean,dd:string) {
this.name = name
this.age = age
this.sub = sub
this.dd = dd
Person.dev()//this访问不了
}
static dev () {
console.log(this.aa);
}
static run () {
this.dev()
}
}
Person.run()
用interface定义一个类
用interface可以定义一个类,使用implements和类关联起来。
interface Person {
run(type: boolean): boolean
}
class Person implements Person {
run(type: boolean): boolean {
return type
}
}
当时用多个interface定义一个类时,用,连接。
interface Person {
run(type: boolean): boolean
}
interface H {
set():void
}
class Person implements Person, H {
run(type: boolean): boolean {
return type
}
set(): void {
}
}
继承父类的写法:
interface Person {
run(type: boolean): boolean
}
interface H {
set():void
}
class A {
params: string
constructor(params) {
this.params = params
}
}
class Person extends A implements Person, H {
run(type: boolean): boolean {
return type
}
set(): void {
}
}
抽象类
使用场景?
抽象类定义abstract,抽象类无法实例化。使用abstract定义的函数,在派生类中要实例化函数,不然会报错(相当于类型定义)
abstract class A {
name:string
constructor(name:string) {
this.name = name
}
abstract getName(): string
}
class B extends A {
constructor() {
super('DO')
}
getName(): string {
return this.name
}
}
let b = new B()
console.log(b.getName());
如果抽象类中函数定义没有使用abstract则可以直接使用。
abstract class A {
name:string
constructor(name:string) {
this.name = name
}
abstract getName(): string
setName(name:string):void {
this.name = name
}
}
class B extends A {
constructor() {
super('DO')
}
getName(): string {
return this.name
}
}
let b = new B()
console.log(b.getName());
b.setName('kuswin')
console.log(b.getName());
元组类型
元组(Tuple)是固定数量的不同类型的元素组合,是数组的变种。
let arr:[string,number] = ['do',1232]
枚举类型(enum)
enum Color {
rad,//0
green,//1
blue//2 默认递增,可以自定义
}
enum Color {
red = 1,//1
green,//2
blue//3
}
相当于:
return type === 'red' ? 0 : type === 'green' ? 1 : 2
异构枚举
混合字符串和数字成员:
enum Types {
No = 'No',
Yes = 1
}
接口枚举:
enum Types {
No = 'No',
Yes = 1
}
interface A {
red: Types.Yes
}
let obj: A = {
red: Types.Yes
}
使用const定义enum,编译出的enum为常量,不适用const 则编译为对象。
枚举类型也可通过value访问key值。(反向一射)
enum Types {
code
}
let code:number = Types.code//0
let key = Types[code]//code
console.log(code,key);
但是字符串类型反向会报错。
类型推论|类型别名type
type可以用来声明一个值的类型,可以联合。
type A = string | number
let a:A= 222
type A = 'off' | 'on'
let a:A= 'off'//只能'off'/'on'
never不存在状态
never来表示不应该存在的状态:
表示这是不可能的事,程序出了问题。
使用场景:
function error(message:string):never {
throw new Error(message)
}
function loop():never {
while(true) {
}
}
也可作为兜底类型。
Symbol类型
let s:symbol = Symbol('哈哈哈哈')
symbol类型内存地址指针位置不同所以是唯一值。
let s:symbol = Symbol('123')
let r:symbol = Symbol('123')
console.log(s === r);//false
Symbol作为对象key值时,不可遍历。
迭代器|生成器
迭代器
type mapKeys = string | number
let arr:Array<number> = [4,5,5]
let set:Set<number> = new Set([1,2,3,4])
let map:Map<mapKeys,mapKeys> = new Map()
map.set('23','kkkk')
map.set(445,'kkkk')
function fn(arg:any) {
let it:Iterator<any> = arg[Symbol.iterator]()//不支持对象
let next:any = {done:false}
while(!next.done) {
next = it.next()
if(!next.done) {
console.log(next);
}
}
}
fn(map)
生成器
for..of
泛型
是为了解决同一个函数,但是类型不一样。
语法为函数名字后面加一个<参数名>,参数名可以随意写。
当使用这个函数的时候把参数的类型传进去就可以(动态类型)
function fn<T> (a:T,b:T):Array<T> {
return [a,b]
}
fn<number>(1,2)
多个类型:
function fn<T,U>(a:T,b:U):Array<T | U> {
return [a,b]
}
fn<number,string>(1,'2')
泛型约束 :
interface Len {
length:number
}
//当时用.length的时候,没有定义参数这个属性,会报错,可以约束一下参数的这个类型
function fn<T extends Len>(arg:T) {
return arg.length
}
fn<Array<number>>([1])
使用keyof约束对象:
首先定义T类型,并使用extends关键字继承object类型的子类型,然后使用keyof操作符获取T的所有键,返回的类型是联合类型,最后使用extends关键字约束K类型必须为keyofT联合类型的子类型。
function prop<T,K extends keyof T>(obj:T,key:K) {
return obj[key]
}
let obj = {
a:1,
b:2,
c:3
}
prop(obj,'a')
prop(obj,'d')//会提示报错 obj内没有定义key值为‘d’的属性,key的类型为 'a' | 'b' | 'c'
泛型类
class Sub<T> {
attr: T[] = [];
add(a:T):T[] {
return [a]
}
}
let sub = new Sub<number>()
sub.attr = [122]
sub.add(333)
namespace命名空间
namespace命名空间可防止全局变量污染,可以嵌套,可以抽离。
三斜线指令///
导入其他文件,导入后可直接使用导入文件的变量,编译后会编译成一个文件。
/// <reference path="./index.ts" />
声明文件引入
/// <reference types="node" />
表明这个文件使用了@types/node/index.d.ts里面声明的名字,这个包需要在编译阶段与声明文件一起被包含起来。同时你需要写一个d.ts文件时才能使用这个指令。
注意事项:
如果你在配置文件 配置了noResolve 或者自身调用自身文件会报错