let str:string = "这是字符串类型"
//上方我们将str这个变量定义为了string类型,如果对他输入其他类型的内容就会报错,例如:
let str:string = 666
//这个就会报错了,会提示你不能将类型"number"分配给类型"string"
let muban:string = `web${str}`
//我们也可以使用ES6中的模板字符串
let u:void = undefined
let u:void = null
//空值类型能够有这两种内容。void的内容也是不能去赋值给别人的
//某种程度上来说,void 类型像是与 any 类型相反
function fnvoid():void{
return//函数也可以定义为空值,如果定义了void则不能返回内容
}
//undefined跟null类型的也可以交换着用的,具体区别放在了下面
八种内置类型
let str: string = "jimmy";
let num: number = 24;
let bool: boolean = false;//这里接收的是布尔值,不是布尔值对象(let b:boolean = new Boolean())
let u: undefined = undefined;
let n: null = null;
let obj: object = {x: 1};
let big: bigint = 100n;
let sym: symbol = Symbol("me");
let anys:any = "小满穿黑丝"
anys = []
anys = 18
anys = {}
anys = Symbol('666')
//any类型就跟原生的是一样的,能够给任意的类型进行定义,所以在在 TypeScript 中,任何类型都可以被归为 any 类型。这让 any 类型成为了类型系统的 顶级类型 (也被称作 全局超级类型 )。
作用的地方:
有时候,我们会想要为那些在编程阶段还不清楚类型的变量指定一个类型。 这些值可能来自于动态的内容,比如来自用户输入或第三方代码库。 这种情况下,我们不希望类型检查器对这些值进行检查而是直接让它们通过编译阶段的检查。 那么我们可以使用 any 类型来标记这些变量
//错误 原始类型(字符串)
let f:object = '努力会获得回报的'
//错误 原始类型(数字)
let g:object = 123
//错误 原始类型(布尔值类型)
let h:object = true
//正确 引用类型(数组类型)
let i:object = [123,"学姐学习Vue3",true]
//正确 引用类型(对象类型)
let j:object = {name:"小满",identity:['B站UP主','二次元','京东员工','全栈开发工程师'],sex:"女"}
//正确 引用类型(函数类型)
let k:object = ()=>"不要对自己pua,相信自己是最棒的,尊重自己,人生更精彩"
{}字面量类型
看起来很别扭的一个东西 你可以把他理解成 new Object 就和我们的第一个 Object 基本一样 包含所有类型
//与Object类型一样
let l:{} = 123//等等,就不写了,跟Object一样
//补充--字面量模式
//这个虽然可以赋值任意类型,赋值结束后,是没办法进行一个修改和增加的操作的
数组类型(TS -- 4)
普通的声明方式
//类型加中括号
let arr:number[] = [123]
//这样会报错定义了数字类型出现字符串是不允许的
let arr:number[] = [1,2,3,'1']
//操作方法添加也是不允许的
let arr:number[] = [1,2,3,]
let arr:number[] = [1,2,3,4];//数字类型的数组
let arr2:string[] = ["1","2","3","4"];//字符串类型的数组
let arr3:any[] = [1,"2",true,undefined,[],{}];//任意类型的数组
let arr4:number[][][] = [[[]],[[]],[[]]]
//这个也能够决定你二维数组还是三维数组想要套几层就写几层
泛型 -- Array <类型>
规则 Array <类型>
let arr1:Array<number> = [1,2,3,4,5]
let arr2:Array<string> = ["1,2,3,4,5"]
let arr3:Array<boolean> = [true]
//泛型数组套娃写法(还能够决定数组里面数组的类型之类的)
let arr4:Array<Array<number>> = [[123],[456]]
const fn = (name:string,age:number):string =>{
return name + age
}
let a = fn('小满',10000)//输入不符合上述参数内定义的类型就会出错
console.log(a)//输出小满10000
--------------------------------------------------------------------
const fn = (name:string,age:number = 666):string =>{//如果在下面使用的时候,没有参数传进来就会以你在这里设置的默认参数执行,比如这个666
return name + age
}
let a = fn('小满')//输入不符合上述参数内定义的类型就会出错
console.log(a)//输出小满666
--------------------------------------------------------------------
const fn = (name:string,age?:number = 666):string =>{//也可以使用这个`?`操作符,age传不传就变成可选的了
return name + age
}
let a = fn('小满穿女仆装')//输入不符合上述参数内定义的类型就会出错
console.log(a)//输出小满穿女仆装
function fn(params:number):void//第一套规则
function fn(params:string,params2:number):void//第二套规则
function fn(params:any,params2?:any):void{
console.log(params)
console.log(params2)
}
let a = fn(1)
//输出1跟undefined,因为遵循的是第一套规则
let a = fn("1",1)
//输出"1"跟1,遵循的是第二套规则
//例如我们的手机号通常是13XXXXXXX 为数字类型 这时候产品说需要支持座机
//所以我们就可以使用联合类型支持座机字符串
let myPhone: number | string = '010-820'
//这样写是会报错的应为我们的联合类型只有数字和字符串并没有布尔值
let myPhone: number | string = true//报错
const regexp:Regexp = /\w\d\s///声明正则
const date:Date = new Date()//对象类型
//const date:Date = new Date().getTime() number类型
const error:Error = new Error('错误')
总结
let b: Boolean = new Boolean(1)
console.log(b)
let n: Number = new Number(true)
console.log(n)
let s: String = new String('小满今天穿白丝')
console.log(s)
let d: Date = new Date()
console.log(d)
let r: RegExp = /^1/
console.log(r)
let e: Error = new Error("error!")
console.log(e)
ES6 提供了更接近传统语言的写法,引入了 Class(类)这个概念,作为对象的模板。通过 class 关键字,可以定义类。基本上,ES6 的 class 可以看作只是一个语法糖,它的绝大部分功能,ES5 都可以做到,新的 class 写法只是让对象原型的写法更加清晰、更像面向对象编程的语法而已。上面的代码用 ES6 的 “类” 改写
JavaScript写法
//定义类 JavaScript写法
class Person {
constructor (name:string,age:number,sub:boolean) {
this.name = name
this.age = age
this.sub = sub
}
}
new Person("小满",22,false)
TypeScript写法
//在TypeScript中是需要提前声明类型的
class Person {
name:string
age:number
sub:boolean//没错,没使用标红的是这些
constructor (name:string,age:number,sub:boolean) {
this.name = name
this.age = age
this.sub = sub//上面定义了变量就需要使用,如果没用使用的话声明的变量就会标红(就算不标红不提示,真运行下去也会报错),不能就那么放着,要么就用上,要么就给他个默认值0塞着
}
}
new Person("小满",22,false)
//在TypeScript中是需要提前声明类型的
class Person {
public name:string
public age:number
public sub:boolean//没错,没使用标红的是这些
constructor (name:string,age:number,sub:boolean) {
this.name = name
this.age = age
this.sub = sub//上面定义了变量就需要使用,如果没用使用的话声明的变量就会标红(就算不标红不提示,真运行下去也会报错),不能就那么放着,要么就用上,要么就给他个默认值0塞着
}
}
let p = new Person("小满",22,false)
p.age p.name p.sub//都可以访问
private
private 私有变量只能在内部访问
//在TypeScript中是需要提前声明类型的
class Person {
private name:string
private age:number
private sub:boolean//没错,没使用标红的是这些
constructor (name:string,age:number,sub:boolean) {
this.name = name
this.age = age
this.sub = sub//上面定义了变量就需要使用,如果没用使用的话声明的变量就会标红(就算不标红不提示,真运行下去也会报错),不能就那么放着,要么就用上,要么就给他个默认值0塞着
}
}
let p = new Person("小满",22,false)
p.age p.name p.sub//都访问不到了
protected
protected内部和子类中访问
provate跟protectd他们的区别是一个是只能在内部使用,一个是内部与子类访问,例子如下
//在TypeScript中是需要提前声明类型的
class Person {
protected name:string
private age:number
public sub:boolean//没错,没使用标红的是这些
constructor (name:string,age:number,sub:boolean) {
this.name = name
this.age = age
this.sub = sub//上面定义了变量就需要使用,如果没用使用的话声明的变量就会标红(就算不标红不提示,真运行下去也会报错),不能就那么放着,要么就用上,要么就给他个默认值0塞着
}
}
class Man extends Person{
constructor(){
super("小满",22,false)
this.name
this.sub//这两个都可以访问到,this.age访问不到。因为age是private,private只能在内部使用而不能在子类访问,Man是Person的子类
}
}
let p = new Person("小满",22,false)
p.age p.name p.sub
abstract class A{
name:string
construct(name:string){//construct:构造器
this.name = name
}
//abstract getName(){//方法getName不能具有实现,因为它标记为抽象。定义抽象类的函数
// return 213
//}
setName(name:string){
this.name = name
}
abstract getName():string//抽象类
}
class B extends A{//派生类。定义了抽象类必须在派生类里实现
//B类是继承A类的,此时A类就是一个抽象类
constructor(){
super('小满')
}
getName():string{
return this.name
}
}
//此时A类是无法被创建实例的(new A),也就是无法创建抽象类的实例
//B类是可以创建实例的(new B)
let b = new B
b.setName("小满2")//通过抽象类的设置,成功修改掉子类的内容
// setName(name:string){
// this.name = name
// }
console.log(b.getName())
元组类型(TS -- 9)
数组合并了相同类型的对象,而元组(Tuple)合并了不同类型的对象。
let arr:[string,number] = ['小满',22]//这样的方式就叫做元组,定义了每个位置需要满足的不同类型
arr[0].length//有
arr[1].length//无,因为上面的定义类型会自动帮我们推断是否有该方法
//Number 类型是没有 length 属性的
越界的元组
当添加的元组越界的时候,越界的类型会被限制为元组类型中每个类型的联合类型
let arr:[string,number] = ['小满',22]//这样的方式就叫做元组,定义了每个位置需要满足的不同类型
arr.push(true)//会报错,因为类型boolean参数不能赋值给string|number的类型
//这个就是元组对越界元素的处理
arr.push('111',2222)//这种就可以
//也可以对二维数组进行限制规定类型
枚举类型(TS -- 10)
在 javaScript 中是没有枚举的概念的 TS 帮我们定义了枚举这个类型
enum 关键字定义枚举
数字定义枚举
默认从0开始的
enum Color{
red,
green,
blue
}
console.log(Color.red,Color.blue,Color.green)//能够得到他们的顺序数字,这里返回0,2,1
增长枚举
能够通过自定义开头决定从哪个数字开始枚举,其他位置的都可以定义,后面的数字就按顺序枚举
enum Color{
red=2,
green,
blue
}
console.log(Color.red,Color.blue,Color.green)//能够得到他们的顺序数字,这里返回2,4,3
type num = 1 extends number ? 1 : 0//返回1
//其中除了number之外,其他类型包括any、unknown、Object、Number也都是返回1
//类型never返回0
//其中的关系图放在下面,他们(类型)是有层级关系的,下面的图由上往下,层级逐级递减
//never返回不了1是因为他是最底层的,连1都比不过,包含不了,所以返回0
never类型(TS -- 12)
TypeScript将使用 never 类型来表示不应该存在的状态
返回never的函数必须存在无法达到的终点
function error(message:string):never {//因为必定抛出异常,所以 error 将不会有返回值
throw new Error(message)
}
function loop():never{
while(true){
//因为这个是死循环,永远不会去返回的
}
}
interface A{
type:"保安"
}
interface B{
type:"草莓"
}
interface C{
type:"卷心菜"
}
type All = A|B
function type(val:All){
while(val.type){
case "保安":break
case "草莓":break
//case "卷心菜":break
default://兜底机制,此时C没有用上就会报错提示。这就算never的作用
const check:never = val
break
}
}
Symbol类型
symbol 是一种新的原生类型,就像 number 和 string 一样
symbol类型的值是通过Symbol构造函数创建的
可以传递参做为唯一标识 只支持 string 和 number 类型的参数
let s:symbol = Symbol('小满')
let num:symbol = Symbol('小满')
let obj = {
[num] : "value",//Symbol
[s] : "草莓",//Symbol
name:"小满",
sex:"男"
}
console.log(obj[num])//取到value
console.log(s,num)//返回Symbol(小满)Symbol(小满)
console.log(s === num)//false
//这个值看似一样,其实因为内存地址指针位置不同,所以是唯一值
for(let key in obj){
console.log(key)
}//只会打印出name跟sex,[num]与[s]将打印不出来
console.log(Object.keys(obj))//["name","sex"]
console.log(Object.getOwnPropertyNames(obj))//["name","sex"],跟上面一样,打印不出来
console.log(JSON.stringify(obj));//["name":"小满","sex":"男"],一样打印不出来
type A = string & number//同时推断A是string和number类型,那显然是不可能的。这时就显示为never类型
//当一个函数抛出异常时,其返回类型为never类型。因为抛出异常时,函数永远不会返回任何值(包括void的值)
function xy():never{//这里选择never,而不是void。void是指没有返回值,但其实还是会抛出错误的
throw new Error("小余")
//或者while死循环
while(true){
//xxx
}
}
//在许多编程语言中,包括 TypeScript,void类型表示函数不返回任何值。这意味着函数执行完毕后不会返回任何内容,而只是执行某些操作或返回到调用方。
//然而,当一个函数抛出异常时,它并没有正常地执行完毕。相反,它会在抛出异常的位置停止执行,并将控制权交回到调用方,因此它不会返回任何值,包括void类型的值。
//在 TypeScript 中,never类型表示“永远不会发生”的值。函数的返回类型为never时,它表明函数不会正常地返回任何值,而是可能抛出异常、无限循环或导致类型错误等情况。
//因此,当一个函数抛出异常时,其返回类型应该是never,而不是void。这有助于编译器在类型检查和推断方面更准确地处理异常情况
never是底层的内容,所以在联合类型上面会有问题
type A = void | number|never//联合类型中never类型会被忽略掉
type A = '唱'|'跳'|'rap'
function ikun(value:A){
switch(value){
case "唱":
break
case "跳":
break
case "rap":
break
default://兜底逻辑
//最后来到这说明前面都没有生效,那就是超出我们预料的情况,这时候估计就是有问题的
const error:never = value;//看你想写啥来提示自己
break
}
}
type mapKeys = string|number//相当于起别名,在下方使用的时候集合了string与number就会相对方便不少
let set:Set<number> = new Set([1,2,3])
let map:Map<mapKeys,mapKeys> = new Map()//这里断言两个mapKeys,一个对应key,一个对应value
map.set('1','小满')
map.set('2','看看腿')
//小迭代器的实现
function gen(erg:any){//这里定义为any类型是因为上面要传到这里的有多种不同类型
let it:interator<any> = erg[Symbol.interator]()
let next:any = {done:false}
while(!next.done){//判断next,由于next默认为fasle,while循环只有true会通过,所以需要取反
next = it.next()//刚开始是声明next给个默认值,等到开始循环的时候再把真正的值赋给他
if(!next.done){
console.log(next);
}
}
}
gen(arr)//调用第一个代码块的arr,输出了与console.log(Iterator.next());一样的内容
//对象是不支持迭代器的使用的,其实我们在控制台输出一个对象,查找他内置的属性,也是找不到Symbol.interator的
type mapKeys = string|number//相当于起别名,在下方使用的时候集合了string与number就会相对方便不少
let set:Set<number> = new Set([1,2,3])
let map:Map<mapKeys,mapKeys> = new Map()//这里断言两个mapKeys,一个对应key,一个对应value
map.set('1','小满')
map.set('2','看看腿')
for (let item of set){
console.log(item)
}//打印出1 2 3
for (let item of arr){
console.log(item)
}//打印出4 5 6
for (let item of map){
console.log(item)
}//打印出['1','小满'] ['2','看看腿']
//其实这就是一个语法糖,将of后面的内容遍历存储到of前面的变量中
如果你使用 any 的话,怎么写都是 ok 的, 这就丧失了类型检查的效果。实际上我知道我传给你的是 string,返回来的也一定是 string,而 string 上没有 toFixed 方法,因此需要报错才是我想要的。也就是说我真正想要的效果是:当我用到id的时候,你根据我传给你的类型进行推导。比如我传入的是 string,但是使用了 number 上的方法,你就应该报错。
使用泛型优化
为了解决上面的这些问题,我们使用泛型对上面的代码进行重构。和我们的定义不同,这里用了一个 类型 T,这个 T 是一个抽象类型,只有在调用的时候才确定它的值,这就不用我们复制粘贴无数份代码了。
其中 T 代表 Type,在定义泛型时通常用作第一个类型变量名称。但实际上 T 可以用任何有效名称代替。除了 T 之外,以下是常见泛型变量代表的意思:
let K1: keyof boolean; // let K1: "valueOf"
let K2: keyof number; // let K2: "toString" | "toFixed" | "toExponential" | ...
let K3: keyof symbol; // let K1: "valueOf"
function getValues<T, K extends keyof T>(person: T, keys: K[]): T[K][] {
return keys.map(key => person[key]);
}
interface Person {
name: string;
age: number;
}
const person: Person = {
name: 'musion',
age: 35
}
getValues(person, ['name']) // ['musion']
getValues(person, ['gender']) // 报错:
// Argument of Type '"gender"[]' is not assignable to parameter of type '("name" | "age")[]'.
// Type "gender" is not assignable to type "name" | "age".
编译器会检查传入的值是否是 Person 的一部分。通过下面的概念来理解上面的代码:
T[K]表示对象T的属性K所表示的类型,在上述例子中,T[K][] 表示变量T取属性K的值的数组
// 通过[]索引类型访问操作符, 我们就能得到某个索引的类型
class Person {
name:string;
age:number;
}
type MyType = Person['name']; //Person中name的类型为string type MyType = string
介绍完概念之后,应该就可以理解上面的代码了。首先看泛型,这里有 T 和 K 两种类型,根据类型推断,第一个参数 person 就是 person,类型会被推断为 Person。而第二个数组参数的类型推断(K extends keyof T),keyof 关键字可以获取 T,也就是 Person 的所有属性名,即 ['name', 'age']。而 extends 关键字让泛型 K 继承了 Person 的所有属性名,即 ['name', 'age']。这三个特性组合保证了代码的动态性和准确性,也让代码提示变得更加丰富了
getValues(person, ['gender']) // 报错:
// Argument of Type '"gender"[]' is not assignable to parameter of type '("name" | "age")[]'.
// Type "gender" is not assignable to type "name" | "age".
type NonNullable<T> = T extends null | undefined ? nerver : T
举例说明
type T0 = NonNullable<string | number | undefined>; // string | number
type T1 = NonNullable<string[] | null | undefined>; // string[]
Parameters
Parameters<T> 的作用是用于获得函数的参数类型组成的元组类型。
定义
type Parameters<T extends (...args: any) => any> = T extends (...args: infer P) => any
? P : never;
举例说明
type A = Parameters<() =>void>// []
type B = Parameters<typeof Array.isArray>// [any]
type C = Parameters<typeof parseInt>// [string, (number | undefined)?]
type D = Parameters<typeof Math.max>// number[]
"use strict"
var A;
(function (A){
A.a = 1;
})(A || (A ={}));
console.log(A,a);
//到此js文件所在目录下使用node index.js运行,输出1
嵌套命名空间
就是提取内容的时候需要多.几次。比如下方,原本只需要a.b,现在需要a.b.value
编译成js文件的话,就是在function又套了一层
namespace a {
export namespace b {
export class Vue {
parameters: string
constructor(parameters: string) {
this.parameters = parameters
}
}
}
}
let v = a.b.Vue
new v('1')
抽离命名空间
将命名空间的内容抽离出来,通过import引入到其他文件中使用
//在index2.ts文件下
expost namespace B{
export const a = 2
}
//在index.ts文件下
import xx from './index2.ts'
namespace A{
export namespace C{
export const D = 5
}
}
console.log(A.C.D,B)//将B抽离成了文件
//将此文件用tsc进行终端编译,然后在tscondig.json将module修改为CommonJs(node.js不认识defined,node.js是基于CommonJS的),进去js文件夹、终端运行node index
简化命名空间
可以给命名空间路径起个名字,然后直接使用这个名字就可以代替命名空间路径了
这个是不能够在ts-node的环境下去使用的
//在index2.ts文件下
expost namespace B{
export const a = 2
}
//在index.ts文件下
import xx from './index2.ts'
namespace A{
export namespace C{
export const D = 5
}
}
console.log(A.C.D,B)//将B抽离成了文件
import AAA = A.C.D
console.log(AAA)//起到跟A.C.D一样的作用
import AAA = A.C
console.log(AAA.D)//起到跟A.C.D一样的作用
命名空间的合并
如果命名空间的命名一样的话(重名),会自动合并
//案例1
namespace A{
export const b = 2
}
namespace A{
export const d = 3
}
//案例2
namespace A{
export const b = 2
export const d = 3//案例1跟案例2是一模一样的,会自动合并
}
interface Name {
name: string
}
interface Age {
age: number
}
interface Sex {
sex: number
}
let people1: Name = { name: "小满" }
let people2: Age = { age: 20 }
let people3: Sex = { sex: 1 }
//Object.assign(a,b,c)
const people = Object.assign(people1,people2,people3)
const watcher:ClassDecorator = (target:Function)=>{
target.prototype.getName = <T>(name:T):T =>{
return name
}
}
@watcher
class A{
}
let a = new A()
a.getName()//会报类型"A"上不存在属性"getName"
//(<any>a).getName()//我们将其断言成any类型
console.log((<any>a).getName("小满深夜骑单车"))//对其进行使用,输出 小满深夜骑单车
@watcher
class B{
}
let b = new B()
console.log(b.getName('666'))//也是可以的
{
"compilerOptions": {
/* Visit https://aka.ms/tsconfig.json to read more about this file */
/* Projects */
// "incremental": true, /* Enable incremental compilation */
// "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */
// "tsBuildInfoFile": "./", /* Specify the folder for .tsbuildinfo incremental compilation files. */
// "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects */
// "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */
// "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */
/* Language and Environment */
"target": "es5", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
// "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */
// "jsx": "preserve", /* Specify what JSX code is generated. */
// "experimentalDecorators": true, /* Enable experimental support for TC39 stage 2 draft decorators. */
// "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */
// "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h' */
// "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */
// "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using `jsx: react-jsx*`.` */
// "reactNamespace": "", /* Specify the object invoked for `createElement`. This only applies when targeting `react` JSX emit. */
// "noLib": true, /* Disable including any library files, including the default lib.d.ts. */
// "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */
/* Modules */
"module": "ES2015", /* Specify what module code is generated. */
// "rootDir": "./", /* Specify the root folder within your source files. */
// "moduleResolution": "node", /* Specify how TypeScript looks up a file from a given module specifier. */
// "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */
// "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */
// "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */
// "typeRoots": [], /* Specify multiple folders that act like `./node_modules/@types`. */
// "types": [], /* Specify type package names to be included without being referenced in a source file. */
// "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
// "resolveJsonModule": true, /* Enable importing .json files */
// "noResolve": true, /* Disallow `import`s, `require`s or `<reference>`s from expanding the number of files TypeScript should add to a project. */
/* JavaScript Support */
// "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the `checkJS` option to get errors from these files. */
// "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */
// "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from `node_modules`. Only applicable with `allowJs`. */
/* Emit */
// "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */
// "declarationMap": true, /* Create sourcemaps for d.ts files. */
// "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */
"sourceMap": true, /* Create source map files for emitted JavaScript files. */
// "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If `declaration` is true, also designates a file that bundles all .d.ts output. */
// "outDir": "./", /* Specify an output folder for all emitted files. */
// "removeComments": true, /* Disable emitting comments. */
// "noEmit": true, /* Disable emitting files from a compilation. */
// "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */
// "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types */
// "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */
// "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */
// "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
// "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */
// "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */
// "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */
// "newLine": "crlf", /* Set the newline character for emitting files. */
// "stripInternal": true, /* Disable emitting declarations that have `@internal` in their JSDoc comments. */
// "noEmitHelpers": true, /* Disable generating custom helper functions like `__extends` in compiled output. */
// "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */
// "preserveConstEnums": true, /* Disable erasing `const enum` declarations in generated code. */
// "declarationDir": "./", /* Specify the output directory for generated declaration files. */
// "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */
/* Interop Constraints */
// "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */
// "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */
"esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables `allowSyntheticDefaultImports` for type compatibility. */
// "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */
"forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */
/* Type Checking */
"strict": true, /* Enable all strict type-checking options. */
// "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied `any` type.. */
// "strictNullChecks": true, /* When type checking, take into account `null` and `undefined`. */
// "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */
// "strictBindCallApply": true, /* Check that the arguments for `bind`, `call`, and `apply` methods match the original function. */
// "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */
// "noImplicitThis": true, /* Enable error reporting when `this` is given the type `any`. */
// "useUnknownInCatchVariables": true, /* Type catch clause variables as 'unknown' instead of 'any'. */
// "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */
// "noUnusedLocals": true, /* Enable error reporting when a local variables aren't read. */
// "noUnusedParameters": true, /* Raise an error when a function parameter isn't read */
// "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */
// "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */
// "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */
// "noUncheckedIndexedAccess": true, /* Include 'undefined' in index signature results */
// "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */
// "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type */
// "allowUnusedLabels": true, /* Disable error reporting for unused labels. */
// "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */
/* Completeness */
// "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */
"skipLibCheck": true /* Skip type checking all .d.ts files. */
}
}
//步骤1:tsc --init 生成tsconfig.json文件
"strictFunctionTypes": false,
/* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */
//翻译:在分配函数时,检查以确保参数和返回值是子类型兼容的
//我们选择了false,对于上面的意思来说也就是不检查子类会不会被父类型兼容了(原本不兼容报错,现在不报错了),dan's
/**
* Make all properties in T optional
将T中的所有属性设置为可选
*/
type Partial<T> = {
[P in keyof T]?: T[P];
};
手写实现
type Person = {
name:string,
age:number,
text:string
}
//keyof:将一个接口对象的全部属性取出来变成联合类型
//keyof的作用就是把我们的属性变成联合类型,在底下就相当于"name"|"age"|"text"。而 in 就是为遍历这个联合类型的每一项,然后放到这个P里(所以P里就是name、age、text),然后使其变成`?`可选的
type Par<T> = {//这个T就是我们传过来的Person,所以T[P]就是Person里面name、age、text的内容(string那些啥的)
//小满对T[P]的形容方式:通过索引取值的方式
[P in keyof T]?:T[P]//所以你在看这个肯定能看懂
};
type p = Partial<Person>//这个时候,我们会发现p上面的属性,name、age、text都变成可选的了
使用前(范例)
type Person = {
name:string,
age:number
}
type p = Partial<Person>
使用后(范例)
type p = {
name?: string | undefined;
age?: number | undefined;
}
Pick
从类型定义 T 的属性中,选取指定一组属性,返回一个新的类型定义。
源码
/**
* From T, pick a set of properties whose keys are in the union K
*/
type Pick<T, K extends keyof T> = {
[P in K]: T[P];
};
手写实现
type Person = {
name:string,
age:number,
text:string,
address:string
}
type Ex = "text" | "age"
type A = Pick<Person,Ex>
分析(需要结合手写的内容看)
type Pick<T, K extends keyof T> = {//T跟K都是泛型,T相当于我们的Person,K就相当于我们传的联合类型,然后同样也经历了keyof的洗礼,使其变成联合类型,K通过extends被约束了这点,使其只能为T,也就是Person内的值
[P in K]: T[P];
};
type p = Pick<Person,'age'|'name'>//这里的Person请参考手写实现的Person
TS 进阶用法 Record & Readonly(TS -- 25)
Readonly
和 Partial 很像是吧?只是将Partial替换成了 Readonly
源码
type Readonly<T> = {
readonly [P in keyof T]: T[P];
};
手写实现
type R<T> = {
readonly [P in keyof T]: T[P];//keyof还是那样,转化为联合类型,in去遍历选项。T[P]通过索引取值的方式
//然后为里面每个内容都加上只读属性
};
type Person = {
name:string,
age:number,
text:string
}
type man = R<Person>
Record
1 keyof any 返回 string number symbol 的联合类型
2 in 我们可以理解成 for in P 就是 key 遍历 keyof any 就是 string number symbol 类型的每一项
3 extends 来约束我们的类型
4 T 直接返回类型
做到了约束 对象的 key 同时约束了 value
源码
type Record<K extends keyof any, T> = {
[P in K]: T;
};
手写实现
type Rec<K extends keyof any,T> = {//T是泛型,传什么在这里并没有限制
[P in K]: T;
};
//keyof返回联合类型
type key = string |number | symbol
type Person ={
name:string,
age:number,
text:string
}
type K = "A"|"B"|"C"//因为我们在这里定义了K,所以let B才只能使用A、B、C,如果这里换成1、2、3,那底下也只能使用1、2、3而不是A、B、C
type B = Rec<K,Person>//这里会返回成type B = {A:Person;B:Person;C:Person;}的形式
let obj:B = {
A:{name:"小满",age:3,text:"三秒真男人"},//这里值的类型需要是Person的类型,因为在type B中已经定义了
B:{name:"小余",age:18,text:"三小时真男人"},
C:{name:"狗洛",age:1,text:"零点三秒真男人"}
}
TS 进阶用法 infer(TS -- 26)
infer 是 TypeScript新增到的关键字 充当占位符
我们来实现一个条件类型推断的例子
定义一个类型 如果是数组类型 就返回 数组元素的类型 否则 就传入什么类型 就返回什么类型
type TYPE<T> = T extends Array<any> ? T[number] : T
type A = TYPE<string[]>//会返回type A = string
type B = TYPE<(string|number)[]>//会返回type B = string|number
type C = TYPE<boolean>//返回type C = boolean
infer
使用 inter 修改
// 是不是某种数组类型 是:返回数组元素类型的类型 不是:返回类型本身
type TYPE<T> = T entends Array<infer U> ? U : T//U不是泛型,而是充当占位符使用,读取Array类型然后进行返回
// 是不是某种数组类型 是:返回数组元素类型的类型 不是:返回never
type TYPE<T> = T entends Array<infer U> ? U : never//限制只能传type T这个元组类型,其他都不能传
type A = TYPE<string[]>//会返回type A = string
type B = TYPE<(string|number)[]>//会返回type B = string|number
type T = [string,number]
//使其变成联合类型(小技巧)
type uni = TYPE<T>//返回联合类型type uni = string|number
type uni = TYPE<T>//返回type uni = never。因为我们进行了限制
infer 类型提取(TS -- 27)
提取头部元素
T extends any[]:对T进行泛型约束,一个any类型的数组
type First<T extends any[]> = T extends [infer one,infer two,infer three]? one:[]:对T进行泛型约束为数组类型,用infer提取,提取的变量名对应着Arr的a、b、c。然后决定返回one,也就是第一个元素还是空数组
type Arr = ['a','b','c']
type First<T extends any[]> = T extends [infer one,infer two,infer three]? one:[]
//ES6进阶版
type First<T extends any[]> = T extends [infer First,...any[]] ? First : []//1 ...
type a = First<Arr>
提取尾部元素
将头尾反过来了
type Arr = ['a', 'b', 'c']
type Last<T extends any[]> = T extends [...any[], infer Last] ? Last : []//... 尾部
type c = Last<Arr>
剔除第一个元素 Shift
将除了第一个之外的其他通过ES6语法提取出来
type Arr = ['a','b','c']
type First<T extends any[]> = T extends [unknown,...infer Rest] ? Rest : []
type a = First<Arr>
剔除尾部元素 pop
将除了第最后一个之外的其他通过ES6语法提取出来
type Arr = ['a','b','c']
type First<T extends any[]> = T extends [...infer Rest,unknown] ? Rest : []
type a = First<Arr>
type Arr = [1,2,3,4]
//先来一个泛型约束(对T),这是通过不断递归拿到最后一个元素(也就是问好后面的那个First)填到最前面,直到没有元素为止(三元表达式)
//
type ReverArr<T extends any[]> = T extends [infer First,...infer rest] ? [...ReverArr<rest>,First] : T
type Arrb = ReverArr<Arr>