1、TS的变量声明
声明了类型之后,TS就会去进行类型的检测
var/let/const 标识符 :数据类型 = 赋值;
let message : string = 'hello world'
2、变量的类型推断
默认的情况下,会将赋值的值的类型,作为前面标识符的类型
let age = 18
console.log(age)
const height = 1.88 //类型的字面量1.88
console.log(height)
注意:使用 let 去声明的变量,推断的就是通用类型
使用 const 声明的变量,推断出来的是字面量类型
3、JS中的类型声明
其中number、string、boolean、null、undefined就直接写就可以了
对于数组和对象,需要去指定每一个具体是声明
// 平时的写法
let list: number[] = [1,2,3];
// 泛型数组
let got: Array<number> = [1,2,3];
// 元组 :是代表一个已知元素数量和类型的数组 就是可以放2个不同类型的元素
let midde: [String,number];
midde=['Ktton',18];
// 对象
const user:{
name:string,
age:number
} = {
name:'crt',
age:18
}
4、TypeScript的数据类型
any类型
在一些情况下我们无法一开始就确定一个变量的类型,它可能在中途会出现一些变化,
所以我们就会去使用any类型
let asg: any = "as写法";
any = 123
any = true
any = {}
unknow类型
和any类似,不同的是,unknown类型的值上做任何操作都是不合法的
let unk :unknown = 'aaa'
unk = 123
// console.log(unk.length);//报错
if(typeof unk === 'string'){
console.log(unk.length); //3
}
void类型
跟java一样 没有返回值但是它有形参
function sun (num1:number,num2:number){
console.log(num1+num2);
}
sun(20,30)
never类型
// never跟它的中文意思一样,永不发生
function foo() :never{
while(true){}
}
function bar():never{
throw new Error()
}
tuple类型 ---元组
元组中每个元素都有自己特性的类型,根据索引值获取到的值可以确定对应的类型
let arr1 :any[] = ['zxy',18,true]
let arrAge = arr1[1]; //后面会详细讲
二、TS的语法细节
1、 可选类型
可以去指定某个属性是否可选(或者不传),加个?就解决
function print(point:{x:number; y:number; z?:number}){
console.log(point.x);
console.log(point.y);
console.log(point.z);
}
// 直接传值
console.log({x:123}); //因为y并没有设置是否可传 所以就会报错
console.log({x:123,y:321}); //123,321
2、 联合查询
联合类型意思就是可以指定多个类型,表示可以是这些类型中任何一个值
function move(id:number | string){
// 应用场景:如果我们想要到id的长度那么,我们就要去类型进行指定
if(typeof id === 'string'){
console.log(id.length);
}else{
console.log(id);
}
}
move(123)
move('abc')
3、类型的别名
使用type关键字 或者是interface(接口)
type Idtype = String | number
function getid(id:Idtype){
console.log(id);
}
type pointType = { x: number; y: number; z?: number }
function printPoint(point: pointType) {
console.log(point.x)
console.log(point.y)
console.log(point.z)
}
4、 type和interface的区别
/*区别在于type类型的使用范围更广,interface只能声明对象
但是interface在声明对象的时候可以去进行追加
而且interface支持继承,继承的效果跟追加是一样的
*/
interface Person{name:string,age:number}
interface Son extends Person{habit:string}
const familed:Son = {name:'丹尼',age:18,habit:'孩子'}
5、交叉类型
交叉相当于就是满足多个类型条件,交叉类型使用&符号
interface man {
name:string
}
interface work{
work:()=>void
}
const me1:man|work={
name:'zzy'
}
const me2:man&work={
name:'zzy',
work:()=>{console.log('吃');
}
}
// 也可以用type关键字去进行别名
type Iperson = man&work
6、类型断言 as(重点)
可以手动改来指定一个值的类型,允许变量从一种类型更改成另一种类型,但是这个转换的类型只能是 更具体 || 不太具体 可以防止不可能的强制转换
let myvalue :any = "hello world";
let value:number = (myvalue as string).length;
console.log(value);
const mingzi :string ='zxy';
// const num:number = mingzi as number; 这种情况编译器会直接报错
const num :number = mingzi as any as number //这种情况就可以
console.log(num);
// 字面量类型(literal types)
const mess :"hell world" = "hell world";
let num1 : 123 = 123;
7、类型缩小
typeof、instanceof、in、switch-case等
三、 TS函数类型
1、函数调用签名
通过编写函数类型的表达式,来表示函数类型使用到的是TS函数中的函数调用签名
type min = (sum1:number,sum2:number) =>number
// 其中=>number 代表的是他们返回值的类型
// 有了函数调用签名就可以在声明变量或函数参数时,指定它们的类型
const sum:min=(a,b)=>a+b
function demo(ropz:min){
console.log(ropz(1,2));
}
demo(sum) //3
2、参数的可选类型
可以指定某个参数不是必传的,可是类型必须写在必传类型的后面
function foo(x:number,y?:number){
console.log(x,y);
}
foo(1,2)
foo(20)
// 这里的y的类型是一个联合类型 number|undefined
// 如果不传数据的话就是undefined
3、默认参数/剩余参数
// 默认参数
// 必传参数 -> 有默认值的参数 -> 可选参数
function oof(x:number,y:number =100){
console.log(x,y);
}
oof(20) //200,100
// 剩余参数
function less(one:number, ...nums:number[]){
let two = one
for(const sums of nums){
two += sums
}
return two
}
console.log(less(20,20)); //40
console.log(less(1,2,3)); // (1+2)+3
console.log(less(1,2,3,4));
4、可推导的this类型
在类或者对象具体类型无法确定的时候可以使用any类型也可以使用this来表示当前对象的类型
const info = {
name:'tody',
eating(){
console.log(this.name +''+'Apple');
}
}
info.eating()
5、不确定的this类型
这里强调一下TypeScript进行类型检测的目的是让我们的代码更加的安全
class MyClass {
private data = 0;
public add(value: number): this {
this.data += value;
return this;
}
public multiply(value: number): this {
this.data *= value;
return this;
}
public getData(): number {
return this.data;
}
}
const myObj = new MyClass();
const result = myObj.add(10).multiply(5).getData();
console.log(result); // 输出:50
/*
在这个例子中,Class 类有两个方法 add 和 multiply,
它们都返回了当前对象的类型 this。这样一来,我们就可以在链式调用方法的时候,
按照任意顺序调用这两个方法,并且结果都会返回一个 MyClass 类型的对象。
最终,我们通过 getData 方法获取计算结果,并输出到控制台中。
需要注意的是,在使用 this 类型时,编译器并不能完全确定 this 的具体类型,
因此需要在代码中谨慎使用,避免出现类型错误或者运行时异常。
同时,在 TypeScript 中,我们也可以使用泛型来代替使用 this 类型,
这样可以更加灵活和安全地处理对象类型的问题。
*/
6、函数的重载
//错误的示范编写一个函数,可以队字符串和数字类型进行相加
// type addtype = number | string
// function add(a:addtype,b:addtype){
// return a + b //这样直接写是错误的,编译器都直接报错
// }
// 可以通过联合类型的逻辑判断去完成
// 但是很麻烦,而且返回值的类型也无法确定
function add (a:string | number, b:string | number){
if(typeof a == 'number' && typeof b == 'number'){
return a + b
}else if(typeof a == 'string' && typeof b == 'string'){
return a + b
}
}
// 我们可以通过重构签名(overload signatures)的方式去进行
function ten (num1:string,num2:string):string
function ten (num1:number,num2:number):number
function ten(num1:any,num2:any):any{
return num1 + num2
}
const cen2 = ten('abc','cba');
const cen1 = ten(10,20);
console.log(cen2); //abccba
console.log(cen1); //30
四、TS类的使用
其实不管是TS还是JS类的使用跟JAVA并没有太大的差别
同行通常开玩笑的说:没有对象?那就new一个
其实没什么好说的,java的基础 科班出身应该都学过
// 类的定义
class Person {
name: string
age: number
constructor(name: string, age: number) {
this.name = name
this.age = age
}
eating() {
console.log(this.name + ' eating')
}
}
const p = new Person('why', 18)
console.log(p.name)
console.log(p.age)
p.eating()
// 类的继承
// 其实就是在类定义好之后加上一个extends的关键字就可以了
class teacher extends Person{}
//继承了之后,可以使用super关键字来去调用父类 super.Person()
// 成员的修饰,在java里面常用就是private public
//只读属性 readonly
// getters/setters 一个是获取/ 一个是监听
// 静态成员,前面加一个static关键字就可以了
五、接口
1、接口的声明
在前面我们通过type可以用来声明一个对象类型
对象的另外一种声明方式就是通过接口来声明
// 接口的声明
interface Person{
name:string,
age:number
}
const info:Person = {
name:'why',
age:14
}
console.log(info);
interface plan{
lable:string
}
function print(obj:plan){
console.log(obj.lable); //输出类型string
}
let myObj = {size:10,lable:"Size 10 obj"}
print(myObj);
2、接口的可选属性
interface man{
name:string,
age:number,
friend?:{
name:string
}
}
const info:man = {
name:'hello',
age:15,
friend:{
name:'world'
}
}
console.log(info.friend?.name);
3、接口的只读属性
接口也可以设置只读属性这样就意味着我们再初始化的时候,这个值无法修改
// 这样就意味着我们再初始化的时候,这个值无法修改
interface man{
readonly name:string,
readonly age:number,
friend?:{
name:string
}
}
const info:man = {
name:'hello',
age:15,
friend:{
name:'world'
}
}
console.log(info.friend?.name); //world
console.log(info.name); //hello
//info.name = '123'; 如果在外面修改,编译器就会出现报错
4、接口的索引类型
//通过 interface 定义索引类型
//[...:类型]:类型
interface IndexLanguage {
[index: number]: string
}
const frontLanguage: IndexLanguage = {
0: 'HTML',
1: 'CSS',
2: 'JavaScript',
3: 'Vue'
}
interface LanguageYeaar {
[name: string]: number
}
const languageYear: LanguageYeaar = {
C: 1972,
Java: 1995,
JavaScript: 1996,
TypeScript: 2014
}
console.log(languageYear['C']); //1972
console.log(frontLanguage[0]); //HTML
5、函数类型
了解一下,非特殊情况通常用类型别名来定义函数
//复习一下类型别名
//type java = {f1:number,f2:number} => number
interface java{
(f1:number,f2:number) :number
}
function classroom (num1:number,num2:number,room:java){
return room(num1,num2) //f1=num1 f2=num2
}
const add:java = (num1,num2)=>num1+num2
console.log(classroom(20,30,add)); //50
6、接口继承
接口是可以多继承,但是类不支持多继承
而且接口继承的同时,这个继承也可以成为限制
interface ISwin{
swimming:()=>void
}
interface ISfoot{
football:()=>void
}
interface ISall extends ISwin,ISfoot{}
const action :ISall={
swimming(){},
football(){}
}
7、交叉类型
原型相当于是联合类型,但是联合类型是只需要符合其中一个就可以 |
但是交叉类型 既可以选择只符合一个,也可以选择同时符合两个 &
type boy = number|string
//交叉类型
interface ISswim{
swimming:()=>void
}
interface ISfly{
flying:()=>void
}
type plan1 = ISswim | ISfly
type plan2 = ISswim & ISfly
const man :plan2={
swimming(){},
flying(){}
}
8、接口的实现
接口定义后,也是可以被类实现的
如果被一个类实现,那么在之后需要传入接口的地方,都可以将这个类传入
interface ISwim {
swimming: () => void
}
const a: ISwim = {
swimming() {}
}
function foo(swim: ISwim) {}
interface IEat {
eating: () => void
}
// 类实现接口
// class Animal {}
// 继承:只能实现单继承
// 实现:实现接口,类可以实现多个接口
class Fish extends Animal implements ISwim, IEat {
// 这里是继承了Animal类,并且实现了ISwim,IEat这两个接口
swimming() {
console.log('Fish Swimmig')
}
eating() {
console.log('Fish Eating')
}
}
class Person implements ISwim {
swimming() {
console.log('Fish Swimmig')
}
}
// 编写一些公关的API:面向接口编程
function swimAction(swimable: ISwim) {
swimable.swimming()
}
// 1. 所有实现了接口的类对应的对象,都是可以传入
swimAction(new Fish())
swimAction({ swimming() {} })
swimAction(new Person())
9、interface和type区别
我们会发现interface和type都可以用来定义对象类型,
如果是定义非对象类型,通常推荐使用type,比如Direction、Alignment、一些Function
如果是定义对象类型,那么他们是有区别的:interface 可以重复的对某个接口来定义属 性和方法;而type定义的是别名,别名是不能重复的;
接口的特性:
不能被实例化、含有声明但未实现的方法
一个类可以实现多个接口,接口支持多继承
接口定义的成员必须要实现,接口中所有的成员都默认是public
六、枚举
枚举允许定义一组命名常量,常量可以是数字也可以是字符
通过enum关键字去实现枚举定义
枚举就是讲一组可能出现的值,一个一个的列举出来,定义在一个类型中
enum move{
LEFT,
RIGHT,
TOP,
BOTTEM
}
function DD(ming:move){
switch(ming){
case move.LEFT:
console.log('向左移动');
break
case move.RIGHT:
console.log("向右移动");
break
case move.TOP:
console.log("向顶部移动");
break
case move.BOTTEM:
console.log("向下移动");
break
default:
console.log("结束");
}
}
console.log(DD(move.LEFT));
console.log(DD(move.RIGHT));
console.log(DD(move.BOTTEM));
console.log(DD(move.TOP));
1、泛型的类型参数化
就是一开始不去刻意的定义类型,而且在调用参数的时候,再去决定它的类型是什么
具体的写法是:<类型>方法
function sum<Type>(num:Type):Type{
return num
}
// 同时对于参数的调用方法有2种
//1. 明确的去传入类型
sum<number>(20)
sum<{ name: string }>({ name: 'why' })
sum<any[]>(['abc'])
// 2.通过类型推导
sum(20)
sum('abc')
2、泛型的类型补充
T:Type的缩写,类型
K、V:key和value的缩写,键值对
E:Element的缩写,元素
O:Object的缩写,对象
function foo<T, E, O>(arg1: T, arg2: E, arg3: O) {}
foo<number, string, boolean>(10, 'abc', true)
3、泛型接口
// 使用泛型去创建接口
interface Person<T1 = string, T2 = number>{
name:T1,
age:T2
}
const kid:Person<string,number>={
name:'why',
age:15
}
const p:Person={
name:'go',
age:14
}
4、泛型的约束
有时候我们希望传入的类型有某些共性,但是这些共性可能不是在同一种类型中
比如string和array都是有length的,或者某些对象也是会有length属性的
那么只要是拥有length的属性都可以作为我们的参数类型,那么应该如何操作呢
interface ISlength{
length : number
}
function getLegth<T extends ISlength>(age:T){
return age.length
}
//泛型函数getLegth,它所接受参数的类型为 T ,并且该参数被要求实现ISlength接口
// 就是必须要拥有一个length属性
// 所以为了满足这一个条件,所返回的类型属性必须要有length这一属性才能传递给函数getLegth
console.log(getLegth('abc')); //因为字符串具有length这一属性 3
console.log(getLegth({length:100})); // 100
//console.log(getLegth(100)); //数字就不行了 会报错
5、模块开发和命名空间
TS可以通过模块开发/命名空间来控制作用域
// 模块开发
// TS可以通过模块开发来控制作用域
export function f1(num1:number,num2:number){
return num1 + num2
}
export function f2(num1:number,num2:number){
return num1 -num2
}
// 命名空间
// 命名空间namespace
// 在vuex仓库中通过true 和false来去控制
export namespace time{
export function time1(foo:string){
return '2023-1-1';
}
}
export namespace what{
export function time2(foo:string){
return '2023-2-1';
}
}
console.log(time.time1('')); // 2023-1-1
console.log(what.time2('')); // 2023-2-1