一:TypeScript是什么 ?
- TypeScript 是微软开发的开源编程语言, 它是JavaScript的一个超集,简称
TS,本质上向这个语言添加了可选的静态类型和基于类的面向对象编程。 - TypeScript 起源于使用JavaScript开发的大型项目 。由于JavaScript语言本身的局限性,难以胜任和维护大型项目开发。因此微软开发了TypeScript ,使得其能够胜任开发大型项目。
- 在JS基础上, 为JS添加了类型支持。TypeScript =
Type+ JavaScript - JS和TS的区别:js属于动态类型的编程语言,TS属于静态类型的编程语言。
二: 安装和使用
- 全局安装TypeScript工具包
npm install -g typescript
2.检测TypeScript的版本
tsc -v
3.编译TypeScript文件
tsc xxx.ts
小结: 要运行.ts文件,先安装全局TypeScript工具包,该工具包提供tsc命令,通过tsc -v检测工具包的版本,tsc xxx.ts将.ts文件转化为js文件
三:TS的初体验
Ts的类型
JS已有的类型(Ts也包括):
- 原始类型:number string boolean null undefined symbol
- 对象类型:object (包括 数组 对象 函数 等...)
TS新增类型: - 联合类型
- 自定义类型(类型别名)
- 接口
- tuple(元组类型)
- 字面量类型
- enum(枚举类型)
- void
- any(任意类型)
- unkown
1.原始类型
//number类型
let count: number = 10;
//string 类型
let name :string = '我是小华'
//boolean 类型
let isLoading: boolean = false
//undefined 类型
let un: undefined = undefined
// null
let timer:null = null
// symbol
let uniKey:symbol = Symbol()
2.联合类型
let timer:null | number = null // 联合类型
timer = setInterval(()=>{},1000) // 定时器的返回值是一个number类型
3.类型别名
作用-> 简化代码 --- 给类型起别名 ---定义新类型
// 类型别名 *** //偷懒用的
type s = string
const str:s ='abc'
type NewType = string |number
const arr :newType = 'abc'
const arr1 :newType = 12
别名可以是任意的合法字符串,一般首字母大写
4.数组类型
// 第一种 简单
// let arr2 :string[] = ['a','b',100] //100报错
let arr2 :string[] = ['a','b']
// 第二种 相对复杂
let arr3 : Array<string> = ['a','bc']
// 定义一个数组元素可能是字符串类型 也可能是数值类型
type newArrayType = string | number
let arr4 : Array<newArrayType> = ['a','b',100]
let arr5 : newArrayType[] = ['a','b',123]
5.函数类型
// 普通函数
function add(a:number=100, b:number=200){
return a+b //类型推论出返回值的类型
}
function add1(a:number=100,b:number=200):number{
//小括弧后面的类型规定的是返回值的类型
return a+b
}
// 箭头函数
const add2 =(a:number=100,b:number=200):number=>{
return a+b
}
// 批量定义同一个函数
type fn = (a:number,b:number)=>number
const add3 :fn=(a,b)=>{
return a+b
}
const sub :fn =(a,b)=>{
//(a,b)里的形参应该要与fn自定义类型中的参数数量和类型保持一致
return a-b
}
// 可选参数和必选参数
function axx(a:number,b?:number){
console.log(a,b);
}
axx(1)
// function zzx(a:number=12,b?:number=11){
//**b?:number=11** 可选参数不能和默认值一起使用
---可选参数后不可再放必选参数
// console.log(a,b);
// }
函数返回值类型void
// 如果什么都不写,此时,add 函数的返回值类型为: void
const add = () => {}
// 如果return之后什么都不写,此时,add 函数的返回值类型为: void
const add = () => { return }
const add = (): void => {
// 此处,返回的 undefined 是 JS 中的一个值
return undefined
}
// 这种写法是明确指定函数返回值类型为 void,与上面不指定返回值类型相同
const add = (): void => {}
void和undefined的区别
function add(a:number, b:number): undefined { // 这里会报错
console.log(a,b)
}
如果函数没有指定返回值,调用结束之后,值是undefined的,但是不能直接声明返回值是undefined
TS函数类型区别于JS函数的一点就是 TS具有函数重载
函数重载是使用相同名称和不同参数数量或类型创建多个方法
let obj: any = {};
function ppl(val: string): void;
function ppl(val: number): void;
function ppl(val: any): void {
if (typeof val === "string") {
obj.name = val;
} else {
obj.age = val;
}
}
ppl("hahaha");
ppl(9);
console.log(obj);
注意:
- 当 TypeScript 编译器处理函数重载时,它会查找重载列表,尝试使用第一个重载定义。 如果匹配的话就使用这个。 因此,在定义重载的时候,一定要把最精确的定义放在最前面,最后函数实现时,需要使用
|操作符或者?操作符,把所有可能的输入类型全部包含进去,用于具体实现。- 函数重载真正执行的是同名函数最后定义的函数体 在最后一个函数体定义之前全都属于函数类型定义 不能写具体的函数实现方法 只能定义类型
6.对象类型
// 对象类型
const s:{
name:string
hello():void //普通函数
run:()=>void // 箭头函数
}={
name:'华',
hello:function () {
console.log('nihao');
},
run:()=>{
console.log('跑');
}
}
配合别名使用 ---简化
// 简化
type Stu={
name:string
sex:string
score:number
height:number
study():void
play:()=>void
}
const stu1:Stu={
name:'阿斯顿',
sex:'男',
score:99,
height:175,
study:function(){
console.log('我在学习');
},
play:()=>{
console.log('我在打游戏');
}
}
const stu2:Stu={
name:'李世',
sex:'男',
score:89,
height:171,
study:function(){
console.log('我没在学习');
},
play:()=>{
console.log('我没在打游戏');
}
}
对象解构和展开运算符
let person = {
name: "小华",
gender: "男",
address: "武汉",
};
// 解构
let {name,gender} = person
//组装对象
let NewPerson = {...person,age:18}
///展开
let {name,...rest}=person
7.接口
关键词 interface 只能指定对象类型 可以继承 在type之前出来, 在无需继承的场景下可以使用type代替,
接口名称(比如,此处的 IPerson),可以是任意合法的变量名称,推荐以 I 开头
interface Istu{
name:string
sex:string
score:number
height:number
study():void
play:()=>void
}
const stu3:Istu={
name:'阿斯顿',
sex:'男',
score:99,
height:175,
study:function(){
console.log('我在学习');
},
play:()=>{
console.log('我在打游戏');
}
使用接口的继承
//格式
interface 接口2 extends 接口1 {
属性1: 类型1, // 接口2中特有的类型
...
}
//例子
interface Point2D { x: number; y: number }
// 继承 Point2D
interface Point3D extends Point2D {
z: number
}
继承后,Point3D 就有了 Point2D 的所有属性和方法(此时,Point3D 同时有 x、y、z 三个属性)
8.元祖
元组可用于定义具有有限数量的未命名属性的类型。每个属性都有一个关联的类型。使用元组时,必须提供每个属性的值
作用-->约定了元素的个数---约定了特定索引对应的数据类型
//简单
const arraylist :[number,string]=[100,'abc']
//对象
type Position1 = { dimension: number, longitude: number }
let p1:Position = {dimension:116.2317, longitude: 39.5427}
//数组
type Position2 = Array<number>
let p2 Position2 = [116.2317, 39.5427]
简单模拟定义一个useState (只有number类型)
const useState =(list:number):[number,(newlist:number)=>void]=>{
const fn=(newlist:number)=>{
list = newlist
}
return [list,fn]
}
const [list,uselist] = useState(100)
console.log(list,'list');
uselist(100)
9.字面量类型
let s1 ='hellow Ts'
const s2 = 'hellow Ts'
//联合使用
type Gender = 'girl' | 'boy'
let g1 :Gender = 'boy'
let g2 :Gender = 'girl'
解释:
- s1是一个变量 值可以说任意字符串 类型为string
- s2是一个常量 值不能变化 类型为 'hellow TS'
- 'girl'和'boy'就是一个字面量类型 某个特定的字符串可以作为TS的类型
- 字面量一般和联合类型一起使用,表示只能取某些个特定的值。
10.enum(枚举类型)
定义:一个被命名的整型常数的集合,用于声明一组命名的常数
格式:
enum 枚举名{
标识符①[=整型常数],
标识符②[=整型常数],
...
标识符N[=整型常数],
}枚举变量;
1.数字枚举
注:当声明一个枚举类型并且没明确赋值时,TS默认为number类型 并且从下标0开始依次累加:
///没有明确赋值时
enum Direction {
Up, // 值默认为 0
Down, // 值默认为 1
Left, // 值默认为 2
Right // 值默认为 3
}
console.log(Direction.Up === 0); // true
console.log(Direction.Down === 1); // true
console.log(Direction.Left === 2); // true
console.log(Direction.Right === 3); // true
//给第一个赋值后 后面的值也会根据前一个值进行累加1
enum Direction {
Up = 10,
Down,
Left,
Right
}
console.log(Direction.Up, Direction.Down, Direction.Left, Direction.Right); // 10 11 12 13
2.字符串枚举
enum Direction {
Up = 'Up',
Down = 'Down',
Left = 'Left',
Right = 'Right'
}
console.log(Direction['Right'], Direction.Up); // Right Up
///当其中一个变量为字符串时 其他变量也要赋值字符串 否则会报错
enum Direction {
Up = 'UP',
Down, // error TS1061: Enum member must have initializer
Left, // error TS1061: Enum member must have initializer
Right // error TS1061: Enum member must have initializer
}
3.组合使用(number和string)
enum NandStrEnum{
abc='abc',
score=99,
}
4.合并操作
enum Direction {
Up = 'Up',
Down = 'Down',
Left = 'Left',
Right = 'Right'
}
enum Direction {
Center = 1
}
///JS代码:
var Direction;
(function (Direction) {
Direction["Up"] = "Up";
Direction["Down"] = "Down";
Direction["Left"] = "Left";
Direction["Right"] = "Right";
})(Direction || (Direction = {}));
(function (Direction) {
Direction[Direction["Center"] = 1] = "Center";
})(Direction || (Direction = {}));
得出结论:Direction对象属性会叠加
简单的枚举类型应用场景
enum Gender {
girl,
boy
}
type User = {
name: string,
gender: Gender
}
const u1: User = {
name: '小花',
gender: Gender.girl // 写代码的时候,可以利用代码提示
}
console.log(u1)
11.any类型
注:顾名思义,any: 任意的。当类型设置为 any 时,就取消了类型的限制。
let obj: any = { x: 0 }
obj.bar = 100
obj()
const n: number = obj
并不推荐使用any 因为会让TypeScript 变为 AnyScript
12.类型断言
作用-->手动指定值得类型
使用场景: 有时候你会比 TS 更加明确一个值的类型,此时,可以使用类型断言来指定更具体的类型。
as语法
例子1:
const box = document.getElementById('img') as HTMLImageElement
console.log(box.src);//类型'HTMLElement'上不存在属性src 给box上加 as HTMLImageElement
// document.createElement('img') -----HTMLImageElement
例子2:
知道后端的结果会是一个类型,但这个结果是用ajax拿到的,不好直接给初始值,又希望得到输出提示的效果
type obj12={
name:string,
age:number
}
let u1 = {} as obj12
console.log(u1.name)
尖括号语法
let someValue = "abcdefg";
let strLength: number = (<string>someValue).length;
非空断言
操作符: !
作用-->
在上下文中当类型检查器无法断定类型时,一个新的后缀表达式操作符 ! 可以用于断言操作对象是非 null 和非 undefined 类型
(1)忽略undefined和null 类型
function myFn(mystring: string | undefined | null) {
const onlyString: string = mystring; // Error
const ignore: string = mystring!; // Ok
}
(2)调用函数时忽略 undefined 类型
type NumGenerator = () => number;
function myFn(numGenerator: NumGenerator | undefined) {
const num1 = numGenerator(); // Error
const num2 = numGenerator!(); //OK
}
13.typeof
作用-->可以用来获取变量或属性的类型
使用场景: 根据已有变量的值,反向推断出获取该值的类型,来简化类型书写
格式:
type 类型 = typeof 常量
例子:
// typeof
let res ={name:'asd',age:12, skills: ['js', 'css'] }
type p = typeof res //读取 res中的类型
function ff(obj:p) {
console.log(obj.skills,'typeof'); //引用了该类型时 会有提示
}
//typeof 只能用来查询变量或属性的类型,无法查询其他形式的类型(比如,函数调用的类型)
14.keyof
作用-->获取某个对象/类型的属性名来构成新类型
格式:
type 类型 = keyof 类型
type 类型 = keyof 对象常量
例子:
type key1 = {x:string,y:number}
type key2 = keyof key1
// key2 ---- x | y
let p:key2='x'
type key3=keyof {a:1,b:2}
// key3 ----"a" | "b" ----获取该对象的a和b (相当于键)
let p1:key3='b'
15.unknown
和any的区别:unknown是更加安全的any类型 例子:
// 没有类型检查就没有意义了,跟写JS一样
// 不安全
let value:any
value = true
value = 1
value.length
解决不能调用unknown的方法:
let value:unknown; // 定义为
value = 'abc';
// 类型断言
console.log( (value as string).length )
// 类型收窄
if (typeof value === 'string') {
value.length
}
unknown和any的主要区别是unknown类型会更加严格 在对unknown类型的值执行大多数操作之前 我们必须进行某种形式的检查 而在对any类型的值执行操作之前 我们不必进行任何检查 所有类型都可以被归为unknown但unknown类型只能被赋值给any类型和unknown类型本身 而any什么都能分配和被分配
泛型(重点)
设计的目的: 设计泛型的关键目的是在成员之间提供有意义的约束,这些成员可以是:类的实例成员、类的方法、函数参数和函数返回值。
泛型(Generics)是允许同一个函数接受不同类型参数的一种模板。相比于使用 any 类型,使用泛型来创建可复用的组件要更好,因为泛型会保留参数类型。
格式:
function 函数名<类型变量1,类型变量2,...>(参数1:类型1,参数2:类型2,...): 返回值类型 {
}
在函数名称的后面写 <>(尖括号),尖括号中添加类型变量
类型变量相当于一个类型容器,能够捕获用户提供的类型(具体是什么类型由用户调用该函数时指定)
----简单的例子
function fn1<T>(value:T):T{return value}//T 为变量 接收变化的类型
// 标准写
const n1=fn1<number>(100)
//简写
const n2=fn1(100) //类型推断出是number类型
----简写一个useState
function useState<T>(value:T) { //简单写
const setValue = (newValue:T):void => {
}
return [value, setValue]
}
let [strs, setStr] = useState('saa') //简单写时 strs会出现不确定的两种类型 string | ((newValue: string) => void)
const [num, setNum] = useState(123) //简单写时 num会出现不确定的两种类型 number | ((newValue: number) => void)
useState--优化
// --多种类型(泛型)----明确返回值类型(元祖)
function useState<S>(value:S):[S,(value:S)=>void] {
type fn=(newValue:S)=>void
const setValue:fn = (newValue) => {
}
return [value, setValue]
}
const [s12, setStr1] = useState('123')
const [num2, setNum1] = useState(123)
泛型接口
interface IdentityFn<T> {
(age: T): T;
}
泛型约束
背景: 默认情况下,泛型函数的类型变量 T 可以代表多个类型,这导致在泛型函数内部无法访问任何属性
简单约束
function fn222<T>(value:T[]):T[]{
console.log(value.length);
return value
}
接口+约束
interface Ilength {length:number}
//继承接口里规定好的属性number类型 就能获取到length 长度
function fn123<T extends Ilength>(value :T):T{
console.log(value.length);
return value
}
应用
//应用--创建函数获取对象中属性的值
function ob<T, kkk extends keyof T>(obj:T,key:kkk){
return obj[key]
}
let jj={name:'qqq',age:12}
ob(jj,'age')
泛型工具
Partial
作用--> 用来基于某个Type来创建一个新类型,新类型中所有的属性是可选的。--统一设置可选
type OldType ={name:string,age:number}
type NewType = Partial<OldType>
// 构造出来的新类型 NewType 结构和 OldType 相同,但所有属性都变为可选的
Readonly
作用--> 用来构造一个类型,将 Type 的所有属性都设置为 readonly(只读)--不可修改。
type Props = {id:string,name:string,age:number}
type ReadonlyProps = Readonly<Props>
let obj1111 :ReadonlyProps={
id:'1',
name:'小龙',
age:12
}
// obj1111.age='12' // 报错--只读属性不能修改
Pick
作用--> 从已有的类型中挑选一组属性,来构造新类型。
<老类型,挑选的属性>
type props ={id:string,name:string,age:number}
type Pickprops = Pick<props,'name'|'age'>
// Pickprops 现在只有 props中的 name 和 age 属性
in
作用-->用来遍历枚举类型
type key = 'q' | 'w' | 'e'
type obj ={
[p in key]
}
infer
作用-->声明一个类型变量并且对它进行使用。
type Rettype<S> = S extends ( ...args: any[] ) => infer R ? R : any;
初体验TS可能会报的错误
console.log报错问题
Cannot find name 'console'. Do you need to change your target library? Try changing thelib compiler option to include 'dom'.
原因:没有创建ts项目,没有写配置文件
解决:用tsc --init命名,在根目录下生成配置文件 tsconfig.json
同名的变量冲突问题
原因:目前写的代码不是模块化的环境,定义的变量都是全局的。
解决:
- 方式1:写代码时,用{ }整体给包起来
- 方式2:export {}