typescript学习笔记(1)

126 阅读17分钟

1.安装方式

安装前提:必须安装NodeJS

全局安装typescript。命令:npm install typescript -g

typescript是以ES3为目标的,但是浏览器基本上都支持es2015(es6)

2.Ts编译为js

2.1在命令台执行tsc hello.ts,执行完后会生成一个hello.js的文件,会出现函数重复的提示。

function greet(person:String,date:Date){
	console.log(`Hello ${person},today is ${date}`)
}
greet('calm',new Date())

2.2解决ts和js冲突问题

tsc --init //生成配置文件

2.3 自动编译

tsc --watch

2.4 发出错误(加入ts出现错误不让编译为js文件)

tsc --noEmitOnError hello.ts

3.ts的严格模式配置

"strict": true,//这个包含所有
"noImplicitAny": true,//any类型判断   单独判断
"scriptNullChecks":true  //null和undefined的校验   单独判断
"target":"es5"//语言环境

4.typescript的常用类型

:+type 称为类型注释 :string :number :boolean

1.string 定义方法:let str:string = 'hello'
2.number 定义方法:let num:number = 0
3.boolean 定义方法:let bool:boolean = true
4.array 定义方法 type[] Array 其中type为任意类型

eg: let arr:number[] = [1,2,3] let arr2:Array = ['a','b','c']

5.any 任何类型(不希望某个特定值导致类型检查错误)
6.function 函数类型
function greet(name:string):void{
	//:string  参数类型注释   :void 返回值类型注释
	console.log('hello'+name)
}

const names = ['张三','李四','王五']
names.forEach((s)=>{
	//此时的S不需要类型注释,根据数组默认被推断为是strin类型
	//整个过程称为上下文类型 因为函数发生在其中上下文通知他应该具有什么类型的上面
	console.log(s.toUpperLowerCase())
})
7.object 对象类型
function printCoord(pt:{x:number,y:number}){
		//:{x:number,y:number}   :{x:number;y:number} {}内可以用,或者;来分隔 参数的类型注释为对象类型
  	console.log('pt的X坐标':pt.x)
  	console.log('pt的Y坐标':pt.y)
}
printCoord({x:3,y:7})

function printName(obj:{name:string,age?:number}){
		//?:number类型  表示当前被修饰的变量值可有可无
  	console.log(obj.age.toUpperLowerCase()) //会报错因为age可能为undefined  
  	console.log(obj.age?.toUpperLowerCase()) //正确的写法
}
printName({name:'zhangsan'})  //不会报错因为age可传可不传

union 联合类型

联合类型是指由两个或者多个类型组成的类型,可能是这些类型中的任意一个值。:number | string
function printId(id:number | string){
	console.log(id)
	if(typeof id === 'string'){
  	console.log(id.toLowerCase(),'')
  }

}

printId(101)
printId('123')

function welcomePeople(x:string[] | string){
	if(Array.isArray(x)){
  	//数组
  }
}
welcomePeople('a')
welcomePeople(['a','b'])

function getFirstThree(x:number[] | string):number[] | string{
	return x.slice(0,3)
}
getFirstThree('abcdefg') // abc
getFirstThree([0,1,2,3,4]) // [0,1,2]
8.类型别名type
当前项目是不允许两个对象或者函数重名。
定义对象类型:type point = {
              	x:number,
              	y:number
              }
              function printCode(pt:point){}
              printCode({
                x:100,
                y:200
              })
定义联合类型 :type ID = string | number
            	function printTpyeId(id:ID){}
定义基元类型: type Str = string
            	function printStr(st:Str):Str{}
9.接口interface
interface point {
  x:number,
  y:number
}
function printCode(pt:point){

}
printCode({
  x:100,
  y:200
})
10.type和interface的区别(所有通过interface来定义的都可以通过type实现)
1.type和interface的相同点:都是用来定义对象或函数的形状。
2.它俩也支持继承,并且可以互相继承。但是具体的形式稍有差别。

interface是通过extends实现的,type是通过&实现的。

3.type和interface的不同点

type可以定义 基本类型的别名,如 type myString = string

type可以通过 typeof 操作符来定义,如 type myType = typeof someObj

type可以申明 联合类型,如 type unionType = myType1 | myType2

type可以申明 元组类型,如 type yuanzu = [myType1, myType2]

interface可以 声明合并, 这种情况下,如果是type的话,就会报 重复定义 的警告,因此是无法实现 声明合并

interface Animal {
	name:string
}
//扩展方法
interface Bear extends Animal { 
	honey:boolean
}
const bear:Bear = {
	name:"winne",
	honey:true
}
console.log(bear.name)//winne
console.log(bear.honey)//true

向现有的类型添加字段
interface Mywindow {
	title:string
}
interface Mywindow  {
	code:number
}
//inteface同时定义两个相同的名称的接口  可以实现合并的来添加新的字段
const w:Mywindow = {
	title:"www',
	number:'445'
}
type Animal = {
	name:string
}
type Bear = Animal & {
	honey:boolean
}
const bear:Bear = {
	name:"winne",
	honey:true
}

向现有的类型添加字段
type Mywindow = {
	title:string
}
//这个会报错 因为type类型创建以后是不能更改的
type Mywindow = {
	code:number
}
//type扩展的字段的方法&
11.类型断言
const myCanvas = document.getElementById('can_content') as HTMLCanvasElement
const myCanvas2 = <HTMLCanvasElement>document.getElementById('can_content')

const x = 'hello' as number  //这样会报错 因为hello被默认获取到的类型是string
const x = ('hello' as any / unknown) as number //any会覆盖number  unknown不知道什么类型
12.文字类型
let testString = 'hw'
testString = 'ohw'
const constant = 'how'

let x:'hello' = 'hello' //此时的x不可以在被更改为其它值,只能是hello,相当于用const定义。

function printText(s:string,alignment:'left' | 'right' | 'center'){
	
}
printText('hello','left')
//数字文字类型
function compare(a:string,b:string):-1|0|1{
	return a === b ? 0 : a > b ? 1 : -1
}
compare()

interface Options {
	width:number
}
funtion configOption(x:Options | 'auto'){}
//调用configOption 可以传入Options对象也可以传入'auto'
configOption({
width:100
})
configOption('auto')
//布尔文字类型
let b1:true = true
let b2:false = false

function handleRequest(url:string,method:'GET' | 'POST' ){}
const req = {
  url:'http://www.baidu.com',
  method:'GET'
}
handleRequest(req.url,req.method) 
//此时的req.method会报红 是因为method定义的是具体的两种类型,
但是req.method只能推断为string类型,所以就不符合'GET''POST'两种文本类型的条件
处理方法就是将req对象的method as'GET'就可以了。
方法一:const req = {
          url:'http://www.baidu.com',
          method:'GET' as 'GET' //推断为文字类型GET
        }
				handleRequest(req.url,req.method) 
方法二:const req = {
          url:'http://www.baidu.com',
          method:'GET' 
        }
				handleRequest(req.url,req.method as 'GET') 
方法三:将req统一的定为文字类型或者const
				const req = {
          url:'http://www.baidu.com',
          method:'GET' 
        } as const
				handleRequest(req.url,req.method) 
13.null和undefined
null 表示不存在
undefined 表示未初始化的值

let x:undefined = undefined
let y:null = null

function doSth(x:string | null){
	if(x === null){}else {}
}

function eg2(x?:number | null){
	console.log(x!.toFixed()) //x!  就表示x不等于null或者undefined的情况下执行
}
14.枚举
enum Diretion {
	Up = 1,
	Down,
	Left,
  Right
}
//设置Up为1  后面的以此类推  就是 2 3 4
console.log(Diretion.Up)  //1
console.log(Diretion.Down)  //2
15.bigInt 和 symbol
bitInt  //非常大的整数
symbol  //全局唯一的引用

const oneH:bigint = BigInt(100)
const anH:bigint = 100n  //只支持es2020

const firstS = Symbol("name")
const secondS = Symbol("name")
firstS  != secondS   因为两个值没有重叠

5.类型缩小

1.类型守卫
function padLeft(padding:number | string,input:string){
	//typeof 类型守卫   
  if(typeof padding === 'number'){
  	return new Array(padding + 1).join(" ") + input;
  }else {
  	return padding + input;
  }
}

typeof strs === 'object' | 'string' | 'number' | 'boolean' | 'bigint' | 'symbol' | 'undefined' | 'function'


function printAll(strs:string | string[] | null){
	if(typeof strs === 'object'){
  	//如果是null也会进这里,因为typeof null  === 'object'
  	//可以实现数组相关的
  }else if(typeof strs === 'string'){
  	//字符串相关
  }else {
  	//null相关
  }
}
2.真值缩小
function testN (n:number){
	if(n){
  	return `${n}人在线`
  }
	return '没人在线'
}
function printAll(strs:string | string[] | null){
	if(strs && typeof strs === 'object'){
  	//可以实现数组相关的
  }else if(typeof strs === 'string'){
  	//字符串相关
  }else {
  	//null相关
  }
}

function multiplyAll(values:number[] | undefined,factors:number){
	if(!values){
		retuen values
  }else {
  	return values.map((v)=>{
      	return v * factors
    })
  }
}

multiplyAll([2,3],3)  //  [6,9]
3.等值缩小
function eg(x:string | number,y:string | boolean){
	if(x === y){
  	//进这里代表类型相等 可以进项字符串相关操作
  }else {
	
  }
}

interface contianer {
 value : number | null | undefined
}
function mulValue(con:contianer,fac:number){
	if(con.value != null){
    //con.value != null  此处的条件 也会判断不等于undefined
  }
}
mulValue({value:5},6)
mulValue({value:undefined},6)
mulValue({value:null},6)
4.in操作符缩小
type Fish = {
	swim:() => void
}
type Bird = {
	fly:() => void
}
type human = {
	swim?:() => void
	fly?:() => void
}

function move(Animal:Fish | Bird | human){
	if('swim' in Animal){
  	//代表就是使用Fish 或 human
		return (Animal as Fish).swim()
  }
	return Animal.fly()
}
5.instanceof操作符缩小
x instanceof Foo  
来检查x的原型链上是否含有Foo.prototype

function logValue(x:Date | string){
	if(x instanceof Date){
  	//代表x就是Date的实例
  }else {
  	//字符串
  }
}
logValue(new Date())
logValue('asd')
6.分配缩小
let x = Math.Random() < 0.5 ? 10 : 'hw!'  //可以看出x是联合类型  let x:string | number
7.控制流分析
function example(){
	let x:string | number | boolean
	x = Math.Random < 0.5 //此时的x的类型为boolean
	if(Math.Random < 0.5){
  	x = 'lee' //此时的x的类型为string
  }else {
  	x = 100  //此时的x的类型为number
  }
	return x
}
let x = example()
x = 'hee'
x = 1000
x= false //这里会报错 因为example 函数里面x的boolean类型会被覆盖
8.使用类型谓词
type Fish = {
	name:string,
	swim:() => void
}
type Bird = {
	name:string,
	fly:() => void
}
//isFish函数的作用是判断传入的是否是Fish类型
function isFish(pet:Fish | Bird):pet is Fish{
	//pet is Fish  称为类型谓词  格式就是函数的形参名  is   类型名称
	return (pet as Fish).swim !== undefined  
	//(pet as Fish).swim !== undefined 条件成立则这个返回值就是Fish不成立则返回值就是Bird
	//pet如果有swim这个类型 那么pet就是Fish
}

function getSmallPet():Fish | Bird {
	let fish:Fish = {
  	name:'sharkey',
  	swim:()=>{}
  }
  let bird:Bird = {
  	name:'sparrow',
  	fly:()=>{}
  }
	return  'fly' in Bird ? bird : fish
}
let pet = getSmallPet()
if(isFish(pet)){
	pet.swim()
}else {
	pet.fly()
}

const zoo:(Fish | Bird)[] = [getSmallPet(),getSmallPet(),getSmallPet(),getSmallPet()]
const underWater:Fish[] = zoo.filter(isFish) | zoo.filter(isFish) as Fish[]
const underWater3:Fish[] = zoo.filter((pet):pet is Fish=>{
	if(pet.name === 'frog'){
  	return  false
  }
	return isFish(pet)
}) 
9.受歧视的unions
interface Shape {
	kind:'circle' | 'square';
	radius?:number;
	sideLength?:number;
}
function handleShape(shape:Shape){
  if(shape.kind === 'square'){
  	
  }
}

function getArea(shape:Shape){
	if(shape.kind === 'circle'){
  	return Math.PI * shape.radius ** 2
  }else {
  	
  }
}
function getArea(shape:Shape){
	swtich(shape.kind)
  	case 'circle':
    	return Math.PI * shape.radius ** 2
  	case 'square':
    	return shape.sideLength ** 2

}
10.nerver类型与穷尽性检查
never类型可以分配给任何类型 但是没有类型可以分配给never
interface Circle {
	kind:'circle';
	radius:number;
}
interface Square {
	kind:'square';
	sideLength:number;
}

type Shape = Circle | Square 
function getArea(shape:Shape){
	swtich(shape.kind)
  	case 'circle':
    	return Math.PI * shape.radius ** 2
  	case 'square':
    	return shape.sideLength ** 2
  	default:
    	//在这里既不是圆形也不是方形的情况下 需要一个默认的default  never类型
    	const temp:never = shape
  		return temp
}
//假如再加一个类型
interface Triangle {
	kind:'triangle';
	sideLength:number;
}

type Shape = Circle | Square  | Triangle
//这种情况函数里的default  的never类型和会报错 是因为此时的default是Triangle类型是存在的

6.函数

1.函数类型表达式
type greatFunction = (a:string) => void
function greater(fn:greatFunction){
  fn('hw')
}
function printG(s:string){
	console.log(s)
}
greater(printG)
2.调用签名
type DescriableFunction = {
	descrition:string;
	(someArg:number):boolean;//这里的参数列表和返回类型之间用的是:而不是=>
}

function dosome(fn:DescriableFunction){
  console.log(fn.descrition+'111'+fn(6))
}

function fn1(n:number){
	console.log(n)
	return true
}
fn1.descrition = 'hello'
dosome(fn1) //6  hello 111 true
3.构造签名
class Ctor	{
	s:string,
	constructor(s:string){
  	this.s = s
  }
}

type someConstructor = {
	new (s:string):Ctor
}

function fn(ctor:someConstructor){
	return new ctor('hello')
}

const f = fn(Ctor)
console.log(f.s) //hello
interface CallorConstructor {
	new (s:string): Date
	(n?:number):number
}

functino fn1(date:CallorConstructor){
	let d = new date('2022-12-12')
	let n = date(100)
}
4.泛型函数
function firstElement<Type>(arr:Type[]):Type | undefined{
  return arr[0]
}
firstElement<string>(['a','b','c']) // <string> 可写可不写 会推断 返回类型为string
firstElement<number>([1,2,3]) // <number> 可写可不写 会推断 一般不用写
firstElement<undefined>([]) // <undefined> 可写可不写 会推断  一般不用写

//泛型的名字可以任意取
//这个函数需要两个参数,函数的返回值是Output
第一个参数是数组arr,
第二个参数是个函数 函数的返回值是Output,函数的参数是arg Input
function map<Input,Output>(arr:Input[],func:(arg:Input)=>Output):Output[]{
	return arr.map(func)
}

const parst  =  map(['1','2','3'],(n) => parseInt(n))  
//根据传参可以推断出input是string  Output是number
function longest<Type>(a:Type,b:Type){
  if(a.length >= b.length){
  	return a 
  }else {
  	return b
  }
}
//Type 上面的写法在调用a.length||b.length的时候会出现没有length属性的可能,
需要在泛型Type后面extends一下
function longest<Type extends {length:Number}>(a:Type,b:Type){
  if(a.length >= b.length){
  	return a 
  }else {
  	return b
  }
}
 
const longArray = longest([1,2],[2,3,4]) //Type就是number
const longArray = longest('aaa','bbbb') //Type就是string
function mininumlength<Type extends {length:Number}>(obj:Type,mininum:number):Type{
  if(obj.length >= mininum){
  	return obj 
  }else {
  	return {length:mininum}
  }
}
const arr = mininumlength([2,3,4],6)
//如果条件成立则可以
function  combine<Type>(arr1:Type[],arr2:Type[]):Type[]{
	return arr1.concat(arr2)
}
const arr = combine([1,2,3],[5,6,7])
const arr = combine([1,2,3],['a','b'])//会报错因为第一个参数已经推断出arr是数字类型数组
const arr = combine<number | string>([1,2,3],['a','b'])//正常运行 不报错
1.可能情况下,使用类型参数本身,而不是对其进行约束。
  function firstElement1<Type>(arr:Type[]){
  	return arr[0]
  }
  
  function firstElement2<Type extends any[]>(arr:Type){
  	return arr[0]
  }
一般写法选择firstElement1
const a = firstElement1([1,2,3])
const b = firstElement2([1,2,3])
2.总是尽可能少的使用类型参数。
function filter1<Type>(arr:Type[],func:(arg:Type)=>boolean){
  	return arr.filter(func)
  }
function filter2<Type,Func extends (arg:Type)=>boolean>(arr:Type[],func:Func){
  	return arr.filter(func)
  }
//一般使用第一种方式 不需要单独定义类型参数
3.如果一个类型参数只出现在一个地方,请重新考虑你是否真的需要它。
function greet<Str extends string>(s:Str){
  	consoloe.log('hello'+s)
  }
greet('word')

function greet2(s:string){
  	consoloe.log('hello'+s)
  }
greet2('word')
5.函数的可选参数
function f(n:number){
	console.log(f.toFixed())
	console.log(f.toFixed(3))
}
f(123.45)
//123
//123.450
f()//直接调用不传参数
function f(n:number = 100){
	console.log(f.toFixed())
	console.log(f.toFixed(3))
}
function f(n?:number){
	console.log(f!.toFixed())
	console.log(f!.toFixed(3))
}
6.回调函数的可选参数
function  myforeach(arr:any[],callback:(arg:any,index?:number)=>void){
	for(lei i = 0;i < arr.length;i++){
    callback(arr[i],i)
  }
}
myforeach([1,2,3],(item,index)=>{
	console.log(item,"ppp")
})
7.函数重载
//重复定义的函数称为函数签名
function makeDate(timestamp:number):Date
function makeDate(m:number,d:number,y:number):Date
function makeDate(mortimestamp,d?:number,y?:number):Date{
	if( d !== undefined && y !== undefined){
  	return new Date(y,mortimestamp,d) 
  }else {
  	return new Date(mortimestamp)
  }
}
//前两个函数称为重载签名
//第三个函数称为实现签名

const d1 = makeDate(123123213132)
const d2 = makeDate(5,6,7)
const d3 = makeDate(6,7) //这里会报错,因为函数参数的个数取决于重载签名函数的参数
function fn(x:string):void
function fn(){

}
fn('hello')//此处调用必须传参,因为函数参数的个数取决于重载签名函数的参数
//重载签名
function fn1(x:boolean):void
function fn1(x:string):void
//实现签名
function fn1(x:booloan | string){
	
}

//返回值类型也需要包含重载签名函数的返回值
function fn2(x:string):string
function fn2(x:boolean):boolean
function fn2(x:string | boolean):string | boolean{
	
}
函数重载准则:
1.在可能的情况下,总是倾向于使用联合类型的参数而不是重载参数
	function len(s:string):number
	function len(arr:any[]):number
	function len(x:any){
  	return x.length
  }

len('wewewe')
len(['1','3'])
len(Math.random() > 0.5 ? 'hello' : [1,2,3]) //这里会报错,因为string | number[] 不能付给string
number[] 不能赋值给string类型  同理 string也不能赋值给any[]类型
//改写
function len(x:any[] | string){
   return x.length
}
interface User { 
	admin:boolean
}
interface DB { 
	filterUsers(filter:(this:User)=>booloean):User[]
}

const db:DB = {
	filterUsers:(filter:(this:User)=>booloean)=>{
    let user1:User = {
    	admin:true
    }
		 let user2:User = {
    	admin:false
    }
		return [user1,user2]
  }
}

const admins = db.filterUsers(function(this:User){
  return this.admin
})
consol.log(admins) //返回这个数组[{admin:true},{admin:false}]
8.需要了解的其他类型
1.void   
当一个函数没有任何返回语句,或者没有从这个返回语句中返回任何明确的值的时候,它都是推断出来void
eg:function noop(){
	return;  //推断返回的是void
}
在javascript中一个不反悔任何值的函数,将隐含的返回undefined这个值
在typescript中voidundefined是不一样的
2.object
object指的是任何的不是基元的值:!(string number booloean bigint symbol null undefined)
小写的object不等同与{},也不等同于Object
在javascript函数值是对象,它们有属性,比如它们的原型链里面有Object.prototype,
是大写的Object一个实例,可以对它调用Object.key等等
object不是Object,在ts中始终使用object
3.unknown
unknown类型代表任何值。这与any类型类似,但更安全,因为对未知的unknown值做任何事情都是不合法的。
eg: function f1(a:any){
	a.b() //不会报错
}
function f2(a:unknown){
	a.b() //报错
}

function f3(s:string):unknown{
	return JSON.Parse(s)
}
const obj = f3(aaa)
4.never
never类型表示永远不会被观察到的值
eg:function f(msg:string):never{
  return new Error(msg);
}
如果一个函数抛出异常,或者中止程序的执行,或者是死循环,那么它的返回值就是never
function f6(x:string | number){
	if(typeof x === 'string'){
	
  }else if(typeof x === 'number'){
  	
  }else {
  	x //never
  }
}
5.Function 
全局性的Function类型描述了诸如bind,call、apply和其他存在于Javascript中所有函数值的属性。它
还有一个特殊的属性,即Function类型的值总是可以被调用,这些调用返回anyfunction f5(f:Function){
  	return f(1,2,3)
}
如果需要接受一个任意函数但是不打算调用最好使用()=>void这种方法
9.参数展开运算符
function f1 (n:number,...m:number[]){
	return m.map(x => n * x )
}
const a = f1(10,1,2,3,4)
//此时的a就是 [10,20,30,40]
const arr1 = [ 1,2,3]
arr1.push(4,5,6)
//arr1 [1,2,3,4,5,6]
const arr2 = [4,5,6]
arr1.push(...arr2)
//arr1 [1,2,3,4,5,6]

const args = [8,5] as const
const angle = Math.atan2(...args)
10.参数结构
function sum({a,b,c}:{a:number,b:number,c:number}){
//不定义类型会下划线提示报错
console.log(a+b+c)
}
sum({a:1,
b:3,
c:5
})

type ABC = {a:number,b:number,c:number}
function sum({a,b,c}:ABC){
//不定义类型会下划线提示报错
console.log(a+b+c)
}
11.返回void类型
一个具有void返回类型的上下文函数类型(type vf=()=>void),在实现时,可以返回任何其它的值,但它会被忽略。
当一个字面的函数,定义有一个void的返回类型时,该函数必须不返回任何东西。


type voidFunc  = () => void
const f1:voidFunc = () => {
	return true
}
const f2:voidFunc = () => true
const f3:voidFunc = function(){
	return true
}
const v1:booloean = f1() //此处会出错 是因为上面的类型已经规定的函数的返回值是void
const v2 = f2()
const v3 = f3()
 function f4() :void{
	return true  //此时会报错 因为返回值必须是void
}
const f5 =  function() :void{
	return true  //此时会报错 因为返回值必须是void
}

7.对象类型

匿名对象
function greet(person:{name:string,age:number}){
	return 'hello' + progress.name
}

接口命名
interface Person {
	name:string,
	age:number
}

function greet(person:Person){
	return 'hello' + progress.name
}

类型别名
type Person = {
		name:string,
	age:number
}

function greet(person:Person){
	return 'hello' + progress.name
}
可选属性
type Shape = {}
interface PaintOptions{
  shape:Shape,
  xPos?:number,
	yPos?:number
}
function  paintShape(opts:PaintOptions){
	let xpos = opts.xPos
	let ypos = opts.yPos

}

const shape:Shape = {}
paintShape({shape})
paintShape({shape,xPos:100})

function  paintShape({shape:Shape,xPos:number=100,yPos=200}:PaintOptions){
//shape:Shape,xPos:number=100,yPos=200  属性别名
	console.log(number)//此时的number是0

}
只读属性
interface SomeType {
	readonly prop:string
}

function dsth(obj:SomeType){
	console.log(obj.prop)
	obj.prop = '' //这里会报错只读类型不可被修改
}


interface Home {
	readonly resident:{
  	name:string,
  	age:number
  }
}

function name(home:Home){
	Home.resident = {}  //这里会报错只读类型不可被修改
	但是Home.residentd.age++   可以修改,只约束了外层对象,不可约束里
	如果给residentd的age添加readonly属性 则residentd.age也就不可修改
}


readonly的属性可以通过别名改变
interface Person {
	name:string,
	age:nunmber
}
interface ReadOnlyPerson {
	readonly name:string,
	readonly age:nunmber
}

let writablePerson:Person = {
	name:'calm',
	age:18
}
let readonlyPerson:ReadOnlyPerson = writablePerson
//此时没有报错,只读属性可以用可写属性赋值
console.log(readonlyPerson.age)
writablePerson.age++
console.log(readonlyPerson.age)
此时会发现年龄加了一岁
索引签名
interace StringArray {
	[index:number]:string
}
const myArray:StringArray = ['a','b'] 值的类型必须和定义的一样为string
const secondItem = myArray[0]
console.log(secondItem,"secondItem")


interface TestString {
	[props:string]:number
}
const testString:TestString = {
	x:100,
	y:200    值的类型必须和定义的一样为Number
}


interface Animal {
	name:string
}
interface Dog extends Animal {
	breed:string
}

interface NotOk {
	[index:string]:number | string, 
	length:number,
	name:string //这里会报错因为类型string 不一颗赋给number  在上面声明联合类型就可以
}
let notOk:NotOk = {
	x:100,
  length:10,
  name:'ddd'
}


interface ReadOnlyStringArray {
	readonly [index:number]:string,
}

let myArray:ReadOnlyStringArray = ['a','b']
myArray[0] = 'fff' //这里会报错,因为只读属性不能被修改
扩展类型
interface BasicAddress {
	name?:string,
	street:string,
	city:string,
	country:string,
	postCode:string
}
interface AddressWithUnit extends BasicAddress {
	unit:string
} //这样AddressWithUnit就具有了所有属性

let awu:AddressWithUnit = {
	unit:3,
	street:'中华街道',
	city:'bj',
	post:'100100',
	country:'china',
	name:'222'
}

interface Colorful {
	color:string
}
interface Circle {
	radius:number
}
interface ColorCircle extends Colorful,Circle {
	
}
const cc:ColorCircle = {
  color:'red',
	radiud:100
}
交叉类型
interface Colorful {
	color:string
}
interface Circle {
	radius:number
}
//定义交叉类型
type ColorCircle = Colorful & Circle; //& 可以实现类型联合
let cc:ColorCircle = {
	color:'red',
	radius:200
}
//
function draw(circle:Colorful & Circle){
  consoloe.log(circle.color)
  consoloe.log(circle.radius)
}
draw({
  color:'red',
  radius:100
})
接口和交叉类型的区别
接口可以使用extends
交叉类型可以用type的&连接
interface sister {
	name:string
}
interface sister {
	age:number
}
定义同名接口的会合并
const s:sister = {
	name:'demon',
	age:34
}

类型别名type不可定义两个相同名称的别名
泛型对象类型
interface Box {
	contents:any
}
let box:Box = {
	contents:'hh'
}
interface Box {
	contents:unknown
}
let x:Box = {
	contents:'hh'
}

console.log(x.contents.toLowerCase())//会报错 因为是unknown类型
解决方法:
if(type of x.contents === string){
	console.log(x.contents.toLowerCase())
}
console.log((x.contents as string).toLowerCase())

 //函数重载
interface numberBox {
	contents:number
}
interface stringBox {
	contents:string
}
interface booleanBox {
	content:boolean
}
function setContents(box:stringBox,newContents:string):void
function setContents(box:numberBox,newContents:number):void
function setContents(box:booleanBox,newContents:boolean):void
function setContents(box:{contents:string | number | boolean},newContents:any):void{
	box.contents = newContents
}

//优雅的泛型来定义
interface Box<Type> {
	contents:Type
}
let boxA:Box<string> = {
  contents:'hh'
}
let boxA:Box<number> = {
  contents:100
}

//
interface Box<Type> {
	contents:Type
}
interface Apple {
}
let a:Apple = {}

type AppleBox = Box<Apple>
let ab:AppleBox = {
	contents:a
}
//通过type定义泛型对象类型
type Box<Type> = {
	contents:Type
}
type orNull<Type> = Type | null
type OneOrMany<Type> = Type | Type[]
type OneOrManyOrNull<Type> = orNull<OneOrMany<Type>> 
type OneOrManyOrNullOrString<Type> = OneOrManyOrNull<string> 

数组类型(泛型)

1.类型操纵
用现有类型或者值来表达新的类型的方法
  泛型类型:带有参数的类型
  keyof:用keyof创建新类型
  typeof:用typeof新类型
  索引访问类型:Type['a']这种语法可以访问类型的子集
  条件类型:可以在类型系统中类似if创建类型
  映射类型:映射现有类型中的每个属性来创建类型
  模板字面量类型:通过模板字面量的字符串来改变属性的映射类型
2.什么是泛型
function identity<Type>(arg:Type):Type {
	return arg
}
let output:identity<string>('mystring')
let output:identity('mystring')
3.使用通用类型
function loggingIdentity<Type>(arg:Array<Type>):Type[] {
console.log(arg.length)
	return arg
}
loggingIdentity('hello')
4.泛型类型
function identity<Type>(arg:Type):Type {
	return arg
}
let myIdentity:<Type>(arg:Type)=>Type = identity
let myIdentity:{<Type>(arg:Type):Type} = identity

//泛型接口
interface GenericIndeentityFn{
	<Type>(arg:Type):Type
}
let myIdentity:GenericIndeentityFn = identity

//泛型接口定义
interface GenericIndeentityFn<Type>{
	(arg:Type):Type
}
let myIdentity:GenericIndeentityFn<string> = identity
5.泛型类
class GenericNumber<NumType> {
  zeroValue:NumType,
	add:(x:NumType,y:NumType)=>NumType
}
//number
let myGeneric = new GenericNumber<number>()
myGeneric.zeroValue = 0
myGeneric.add() = function (x,y){
  return x + y
}

//string
let myGeneric = new GenericNumber<number>()
myGeneric.zeroValue = 'hello'
myGeneric.add() = function (x,y){
  return x + y
}
6.泛型约束
interface lengthWise {
	length:number
}
//Type extends lengthWise  用户传入必须带有Length
function loggingIndetity<Type extends lengthWise>(arg:Type):Type{
	return arg
}

loggingIndetity(['hello','word'])
loggingIndetity('hello')
7.在泛型约束中使用类型参数
//key必须在type中存在
function getProperty<Type,Key extends keyof Type>(obj:Type,key:Key){
	return obj[key]
}
let x  = {
	a:1,
  b:2,
  c:3,
  d:4
}
getProperty(x,'a')
getProperty(x,'m')//这里会报错  x中不存在m