TypeScript——TS简介(面试题)、运行环境、变量声明、基础类型、类型断言、初识接口

(27) 2024-04-18 21:01:01

目录

一、TS简介

1、定义

 2、TS的特征或理解——面试题

1.  类型系统

2. 适用于任何规模

3. 与标准同步

4. 缺点

3、TypeScript是什么——面试题

 4、官网

 二、运行环境

1、环境搭建

1)安装node (推荐16+)

2)全局安装 typescript

3)查看版本  

4)创建tsconfig.js文件

2、编译并运行

 3、直接运行

4、自动编译

5、使用tsconfig进行构建

三、 变量声明

四、 基础类型

1、布尔值

2、数字

3、字符串

4、数组

5、元组Tuple

6、枚举

7、Any

8、Void

9、Null 和 Undefined

10、Never

11、Object

12、类型别名与联合类型

13、类型推论

五、类型断言

六、初识接口

1)类型别名

2)对象接口

1)接口初探

2)可选属性

3)只读属性


对于前端项目来说:

运行环境:node、浏览器 ==>它们都只能运行 js

ts会经过编译,变为js,然后去运行环境运行

ts会经过编译==>

1、typescript官方的翻译器4.8.11,webpack、vite把typescript继承到脚手架环境=>js=>node,浏览器

2、ts-node帮我们把ts转化为js ,然后再用node环境运行js

一、TS简介

1、定义

1.  TypeScript,简称为TS,是JS的超级;Vue3.0就是使用TS开发出来的,并且推荐开发者使用TS进行开发;
2.  TS是可以直接运行的(比如使用ts-node),最终编译为纯的JS运行在任意的平台之上,是开源的;
3.  始于JavaScript,归于JavaScript!

 2、TS的特征或理解——面试题

1.  类型系统

1.TypeScript是静态类型:
类型系统按照「类型检查时机」来分类,可以分为动态类型和静态类型。
动态类型是指在运行时才会进行类型检查,这种语言的类型错误往往会导致运行时错误。JS是一门解释型语言,没有编译阶段,所以JS是动态类型。

2.TypeScript 是弱类型:
    类型系统按照「是否允许隐式类型转换」来分类,可以分为强类型和弱类型,TS是完全兼容JS的,它不会修改JS运行时的特性,所以它们都是弱类型。

例子:
console.log(1 + '1');
// 打印出字符串 '11', ts和js都不会报错

print(1 + '1')
Python 是强类型会报错
# TypeError: unsupported operand type(s) for +: 'int' and 'str' 

2. 适用于任何规模

1.TypeScript 非常适用于大型项目

这是显而易见的,类型系统可以为大型项目带来更高的可维护性,以及更少的 bug。

2.提高开发效率。

在中小型项目中推行 TypeScript 的最大障碍就是认为使用 TypeScript 需要写额外的代码,降低开发效率。但事实上,由于有[类型推论],大部分类型都不需要手动声明了。相反,TypeScript 增强了编辑器(IDE)的功能,包括代码补全、接口提示、跳转到定义、代码重构等,这在很大程度上提高了开发效率。而且 TypeScript 有近百个[编译选项],如果你认为类型检查过于严格,那么可以通过修改编译选项来降低类型检查的标准。

3.TypeScript 还可以和 JavaScript 共存。

这意味着如果你有一个使用 JavaScript 开发的旧项目,又想使用 TypeScript 的特性,那么你不需要急着把整个项目都迁移到 TypeScript,你可以使用 TypeScript 编写新文件,然后在后续更迭中逐步迁移旧文件。如果一些 JavaScript 文件的迁移成本太高,TypeScript 也提供了一个方案,可以让你在不修改 JavaScript 文件的前提下,编写一个[类型声明文件],实现旧项目的渐进式迁移。

4.代码补全和接口提示等功能都是 TypeScript

就算你从来没学习过 TypeScript,你也可能已经在不知不觉中使用到了 TypeScript——在 VSCode 编辑器中编写 JavaScript 时,代码补全和接口提示等功能就是通过 TypeScript Language Service 实现的

5.第三方库原生支持了 TypeScript

一些第三方库原生支持了 TypeScript,在使用时就能获得代码补全了,比如 Vue 3.0  React

3. 与标准同步

1.TypeScript 的另一个重要的特性就是坚持与 ECMAScript 标准同步发展。

ECMAScript 是 JavaScript 核心语法的标准,自 2015 年起,每年都会发布一个新版本,包含一些新的语法。

一个新的语法从提案到变成正式标准,需要经历以下几个阶段:

Stage 0:展示阶段,仅仅是提出了讨论、想法,尚未正式提案。
Stage 1:征求意见阶段,提供抽象的 API 描述,讨论可行性,关键算法等。
Stage 2:草案阶段,使用正式的规范语言精确描述其语法和语义。
Stage 3:候选人阶段,语法的设计工作已完成,需要浏览器、Node.js 等环境支持,搜集用户的反馈。
Stage 4:定案阶段,已准备好将其添加到正式的 ECMAScript 标准中。
一个语法进入到 Stage 3 阶段后,TypeScript 就会实现它。一方面,让我们可以尽早的使用到最新的语法,帮助它进入到下一个阶段;另一方面,处于 Stage 3 阶段的语法已经比较稳定了,基本不会有语法的变更,这使得我们能够放心的使用它

2.TypeScript 的发展历史
2012-10:微软发布了 TypeScript 第一个版本(0.8),此前已经在微软内部开发了两年。
2014-04:TypeScript 发布了 1.0 版本。
2014-10:Angular 发布了 2.0 版本,它是一个基于 TypeScript 开发的前端框架。
2015-01:ts-loader 发布,webpack 可以编译 TypeScript 文件了。
2015-04:微软发布了 Visual Studio Code,它内置了对 TypeScript 语言的支持,它自身也是用 TypeScript 开发的。
2016-05:@types/react 发布,TypeScript 可以开发 React 应用了。
2016-05:@types/node 发布,TypeScript 可以开发 Node.js 应用了。
2016-09:TypeScript 发布了 2.0 版本。
2018-06:TypeScript 发布了 3.0 版本。
2019-02:TypeScript 宣布由官方团队来维护 typescript-eslint,以支持在 TypeScript 文件中运行 ESLint 检查。
2020-05:Deno 发布了 1.0 版本,它是一个 JavaScript 和 TypeScript 运行时。
2020-08:TypeScript 发布了 4.0 版本。
2020-09:Vue 发布了 3.0 版本,官方支持 TypeScript。

4. 缺点

1.有一定的学习成本,需要理解接口(Interfaces)、泛型(Generics)、类(Class)、枚举类型(Enums)等前端工程师可能不是很熟悉的概念;

2.短期可能会增加一些开发成本,毕竟要多写一些类型的定义,不过对于一个需要长期维护的项目,TypeScript 能够减少其维护成本;

3.集成到构建流程需要一些工作量;ts-loader (需要运行,因为不能运行)

4.可能和一些库结合的不是很完美;

3、TypeScript是什么——面试题

  • TypeScript 是添加了类型系统的 JavaScript,适用于任何规模的项目。
  • TypeScript 是一门静态类型、弱类型的语言。
  • TypeScript 是完全兼容 JavaScript 的,它不会修改 JavaScript 运行时的特性。
  • TypeScript 可以编译为 JavaScript,然后运行在浏览器、Node.js 等任何能运行 JavaScript 的环境中。
  • TypeScript 拥有很多编译选项,类型检查的严格程度由你决定。
  • TypeScript 可以和 JavaScript 共存,这意味着 JavaScript 项目能够渐进式的迁移到 TypeScript。
  • TypeScript 增强了编辑器(IDE)的功能,提供了代码补全、接口提示、跳转到定义、代码重构等能力。
  • TypeScript 拥有活跃的社区,大多数常用的第三方库都提供了类型声明。
  • TypeScript 与标准同步发展,符合最新的 ECMAScript 标准(stage 3)。

 4、官网

https://www.tslang.cn/docs/home.html

 二、运行环境

1、环境搭建

1)安装node (推荐16+)

>   nodejs 10+以上就可

(nodejs写npm i -g,丢包了,可以设置一下:控制面板>用户账户>更改用户账户控制设置>拉到从不通知)

2)全局安装 typescript

(打开任意一个小黑窗)

>   npm    i    -g    typescript

3)查看版本  <!--tsc:TypeScriptCompile   TS编译-->

>   tsc    -v

4)创建tsconfig.js文件

>   tsc    --init

{
    "compilerOptions": {
        "target": "es5",  //原本下好是:es2016 
        "noImplicitAny": false,
        "module": "amd",
        "removeComments": false,
        "sourceMap": false,
        "outDir": "./js"
    }
}

2、编译并运行

TypeScript——TS简介(面试题)、运行环境、变量声明、基础类型、类型断言、初识接口 (https://mushiming.com/)  第1张

1)创建

//app.ts 
const hello : string = "Hello World!";
console.log(hello);

2)编译

进入app.ts文件所在目录,执行命令,就会自动生成app.js

$ tsc app.ts

3) 运行

$ node app.js

 3、直接运行

它是自己先转为js文件,再运行,但是文件中不会生成js文件

*安装全局的ts-node模块*

>   npm    install    -g   ts-node      

然后就可以直接执行ts文件了

>   ts-node    app.ts

4、自动编译

自动编译成js文件

终端 -> 运行任务 -> 选择里面的tsc:监视

5、使用tsconfig进行构建

tsc --build tsconfig.json

三、 变量声明

1. 与js相同,使用var、let或const等;  思考:所有变量声明的方式;

2. 声明变量指定类型(任意类型都可),这样的变量赋值时只能使用指定类型的值,以达到强类型语言变量的特点及其优点;

//string
var names:string = '小王'
//number
var age:number = 23;
//bollean
let flag:boolean = true;
flag = 23;//编译出错

四、 基础类型

null undefined  number  boolean  string symbol    void never any

注:这里的基础是指普通的、常规的、内置的的意思;不是JS里面所说的基础类型和引用类型。

1、布尔值

最基本的数据类型,只有简单的true/false两个值,在JS和TS里叫做 Boolean。

let isLogin: boolean = false;
// 编译通过
// 后面约定,未强调编译错误的代码片段,默认为编译通过

let isLogin2: boolean = new Boolean(1);
//编译报错
//使用构造函数 Boolean 创造的对象不是布尔值

2、数字

和JS一样,TS里的所有数字都是浮点数,类型是 number。 支持`2、8、10、16`等各种进制。

不适合进行科学计算;

 还是存在精度问题

let n1: number = 6;
let n2: number = 0xf00d;// ES6 中的十六进制表示法,会被编译为十进制数字
let n3: number = 0b1010;// ES6 中的二进制表示法,会被编译为十进制数字
let n4: number = 0o744;// ES6 中的八进制表示法,会被编译为十进制数字
let n5: number = NaN;
let n6: number = Infinity;
let n7:number;   //也可以不写值

3、字符串

使用 `string`表示文本数据类型。 和JS一样,可以使用双引号( `"`)、单引号(`'`)和反引号(`)表示字符串。总之,和JS一样。

var str="hello"   //隐式推论,通过赋初始值,推测出:var str:string

let myName: string = 'karen';
let myAge: number = 25;
var str="hello"   //隐式推论,通过赋初始值,推测出:var str:string
console.log(str)
str=1000  //这样就不行,不能改类型,初始化已经定位了字符串类型

// 模板字符串
let str: string = `Hello, my name is ${myName}.
I'll be ${myAge + 1} years old next month.`;

字符串字面量类型

字符串字面量类型用来约束取值只能是某几个字符串中的一个。

type EventNames = 'click' | 'scroll' | 'mousemove';
function handleEvent(ele: Element, event: EventNames) {
    // do something
}
handleEvent(<Element>document.getElementById('hello'), 'scroll');  // 没问题
handleEvent(<Element>document.getElementById('world'), 'dblclick'); // 报错,event 不能为 'dblclick'

4、数组

TS像JS一样可以操作数组元素。 有两种方式可以定义数组。

第一种,可以在元素类型后面接上 `[]`,表示由此类型元素组成的一个数组。

let score:number[] = [1,20, parseInt('50')]; 

let arr2:string[] = ["hello","hai"]; 

第二种,
let fruit:Array<string|number> = ['apple', 'banana', 'pear', 20]; 

let score:number[] = [1,20, parseInt('50')];  //第一种
let fruit:Array<string|number> = ['apple', 'banana', 'pear', 20];  //第二种
let arr1: number[] = [1, 1, 2, 3, 5];//正确
let arr2: number[] = [1, '1', 2, 3, 5];//错误
let arr3: string[] = ["hai","hello"];
arr3.push(8);//错误
let arr4: Array<number> = [1, 1, 2, 3, 5];//正确
let arr5: Array<string> = ["hello","h5"];//正确
arr3=arr5   //这个编译不会报错,因为类型是一样的,可以赋值

type mytype1="666"|boolean|100
let arr:Array<boolean|mytype1>;
arr=["666",100,false]

5、元组Tuple

元组类型允许表示一个已知元素数量和类型的数组,各元素的类型不必相同。

比如,你可以定义一对值分别为 stringnumber类型的元组。

数组合并了相同类型的对象,而元组(Tuple)合并了不同类型的对象

//1.默认值
let tom: [string, number] = ['Tom', 25];
//2.通过下标赋值
let tom: [string, number] = ['', 0];
tom[0] = 'Tom';
tom[1] = 25;
//通过下标获取元组的元素并进行对应的操作
tom[0].slice(1);
tom[1].toFixed(2);
//3.越界操作
let tom: [string, number];
tom = ['Tom', 25];
tom.push('male');  //超过个数
tom.push(true);//报错,当添加越界的元素时,它的类型会被限制为元组中每个类型的联合类型

//解构赋值
const lilei: [string, number] = ["Lilei", 23];
console.log(lilei);
const [myname, myage]: [string, number] = lilei;  //隐式:myname:string=lilei[0]
console.log(myname, myage);

6、枚举

`enum`类型是对JS标准数据类型的一个补充。 使用枚举类型可以为一组数值赋予友好的名字。

枚举类型用于取值被限定在一定范围内的场景,比如一周只能有七天,颜色限定为红绿蓝等。
枚举成员会被赋值为从 0 开始递增的数字,同时也会对枚举值到枚举名进行反向映射,也可以手动的指定成员的数值;

//性别 一般是存储为数字的
enum Gender {Secret, Male, Female};
console.log(Gender.Male);

//一周的每天
enum Days {Sun, Mon, Tue, Wed, Thu, Fri, Sat};
console.log(Days.Sun, Days.Mon, Days.Tue, Days.Sat);

console.log(Days["Sun"] === 0); // true
console.log(Days["Sat"] === 6); // true
//根据编号得到元素名
console.log(Days[0] === "Sun"); // true
console.log(Days[6] === "Sat"); // true

//商品的状态  可以手动设置初识值  或 每个都设置具体的值
enum orderStatus {Car=-1, Order=5, Payed, Deliver, Receive, Comment, Return};  //前面两个给了默认值,第三个就是6
console.log(orderStatus.Payed, orderStatus.Comment);  //6 9
console.log(orderStatus[5])  //Order

7、Any

有时需要为那些在编程阶段还不清楚类型的变量指定一个类型。 这些值可能来自于动态的内容,比如来自用户输入或第三方代码库。

这种情况下,我们不希望类型检查器对这些值进行检查而是直接让它们通过编译阶段的检查。 那么我们可以使用 `any`类型来标记这些变量:

let notSure: any = 4;
notSure = "maybe a string instead";
notSure = false; // okay, definitely a boolean

可以认为,声明一个变量为任意值之后,对它的任何操作,返回的内容类型都是任意值。

let anyThing: any = 'hello';
console.log(anyThing.myName);  //und,不会报错,因为anyThing是任意类型,可以取
console.log(anyThing.myName.y); //und.y   编译是通过的,但是会报错

某种类型的数据,可以赋值给any类型的变量

任意类型的变量也可以赋值给某种类型的数据

let b:any=100;
let c:string;
c="hello";
c=b;

8、Void

void,无效的,表示没有任何类型。 当一个函数没有返回值时,其返回值类型是 `void`;

function warnUser(): void {
    console.log("This is my warning message");
}

声明一个`void`类型的变量没什么用,因为你只能为它赋予`undefined`和`null`:

let unusable: void = undefined;

9、Null 和 Undefined

undefinednull两者各自有自己的类型undefined和null。 和 void相似,用处不大:

undefinednull两者可以赋值给任何类型的变量

10、Never

`never`类型表示的是那些永不存在的值的类型。

例如, `never`类型是那些总是会抛出异常或根本就不会有返回值的函数表达式或箭头函数表达式的返回值类型;

// 返回never的函数必须存在无法达到的终点
function error(message: string): never {
    throw new Error(message);
}

// 推断的返回值类型为never
function fail() {
    return error("Something failed");
}

11、Object

`object`表示非原始类型,也就是引用类型,是除`number`,`string`,`boolean`,`symbol`,`null`或`undefined`之外的类型,可以使用"对象", "数组对象"、string、number等为其赋值,null和undefined不行的。  

之所以Object变量可以接收任何对象,是因为Object是所有类的父类。

let a:object = [1, 2, 3]
console.log(a,typeof a);  //[ 1, 2, 3 ]  object
a = {id:1, name:'karen'}
console.log(a,typeof a);   //{ id: 1, name: 'karen' }  object
a = '{id:1, name:"karen"}'  //报错  ,因为它不是对象
console.log(a,typeof a);   

12、类型别名与联合类型

多个类型的联合,它们之间是或的关系。

type manyType = number | boolean | string;
let a: manyType
a = 0o700;
console.log(a);  //228
a = true;
console.log(a);  //true
a = '字符串';
console.log(a);  //字符串

13、类型推论

没有声明类型的变量被赋值一次后,变量的类型就确定了,之后再次被赋值时,不可改变赋值的类型。

let r = 100;//确定了r的类型为number
r = "fds";//编译报错,因为r的类型为number

五、类型断言

有些时候,我们需要做一些“自己比编译器更清楚的知道自己在做什么”,这个时候就用到断言

类型断言只能够「欺骗」TypeScript 编译器,无法避免运行时的错误。

类型断言不是类型转换,断言成一个联合类型中不存在的类型是不允许的。

语法:  **值  as  类型**  或者  **<类型>值**

类型断言有两种形式。 其一是“尖括号”语法:

let someValue: any = "this is a string";
let strLength: number = (<string>someValue).length;

另一个为`as`语法:

let someValue: any = "this is a string";
let strLength: number = (someValue as string).length;
async function  fn ():any {
	let res=await axios("/test")
	return res.data
}
let re=fn()
(re as string[])==>等价<string[]>re

六、初识接口

TS的核心原则之一就是对值所具有的结构进行类型检查。TS里面接口的作用就是为这些类型命名 和 为你的代码 或 第三方代码定义契约。

在面向对象语言中,接口(**Interfaces**)是一个很重要的概念,它是对行为的抽象,而具体如何行动需要由类(classes)去实现(implement)。

TypeScript 中的接口是一个非常灵活的概念,除了可用于[对类的一部分行为进行抽象]()以外,也常用于对「对象的形状(Shape)」进行描述。

1)类型别名

type person={
	age:number,
	name:string
}
let obj:person;
obj={age:20,name:"karen"}
obj={age:100,name:"jack"}

2)对象接口

interface dog{
	age:number,
	name:string,
	color?:string
}
let d1:dog;
d1={
	age:100,
	name:"hello",
	color:"red"
}
d1={
	age:200,
	name:"karen"
}
console.log(d1)  //{ age: 200, name: 'karen' }
interface box{
	color:string,
	witdh:number
}
type mytype1=box;
let a:mytype1;
type mytype2=box|any[];
let b:mytype2;

1)接口初探

function printLabel(labelledObj: { label: string }) {
  console.log(labelledObj.label);
}

let myObj = { size: 10, label: "Size 10 Object" };
printLabel(myObj);

 `printLabel`有一个参数,类型检查器会要求这个对象参数有一个名为`label`类型为`string`的属性。 注意:我们**传入的对象参数可能会包含很多属性**,但编译器只会检查那些必需的属性是否存在,以及其类型是否匹配。

接下来,我们用接口来约束参数的类型结构(对象的形状):

1.  ​    只关注值的外形, 只要传入的对象满足提到的条件,那么它就是被允许的。
2.  ​    对象是无序的:类型检查器不会去检查属性的顺序,只要相应的属性存在且类型是对的就可以。

interface labelledVale{
    label:string;
    size:number;
    show?:boolean;
}
function printLabel(labelledObj: labelledVale) {
    console.log(labelledObj.label);
}

2)可选属性

接口里的属性不全都是必需的。 有些是只在某些条件下存在,或者根本不存在。 可选属性在应用“option bags”模式时很常用,即给函数传入的参数对象中只有部分属性赋值了。

interface SquareConfig {
  color?: string;
  width?: number;
}

function createSquare(config: SquareConfig): {color: string; area: number} {
  let newSquare = {color: "white", area: 100};
  if (config.color) {
    newSquare.color = config.color;
  }
  if (config.width) {
    newSquare.area = config.width * config.width;
  }
  return newSquare;
}

let mySquare = createSquare({color: "black"});

带有可选属性的接口与普通的接口定义差不多,只是在可选属性名字定义的后面加一个?符号。好处:

  1. 可以对可能存在的属性进行预定义;

  2. 可以捕获引用了不存在的属性(往往是无意之中敲错的代码)时的错误;

3)只读属性

一些对象属性只能在对象刚刚创建的时候修改其值。 你可以在属性名前用 `readonly`来指定只读属性:

注:TypeScript具有`ReadonlyArray<T>`类型,它与`Array<T>`相似,只是把所有可变方法去掉了,因此可以确保数组创建后再也不能被修改。

**`readonly` vs `const`**:做为变量使用的话用 `const`,若做为属性则使用`readonly`。

interface Box{
	color:string,
	width?:number,
	readonly positon:string,  //必写只读,必须写
	readonly bgc?:string      //可选只读,可以不写,但是如果要写,就必须在初始化创建的时候就写
}
let b1:Box={
	color:"red",
	width:20,
	positon:"relative" //初始化
}
b1.color="blue"
// b1.positon="fixed";  //不行只读属性不能修改
//虽然positon不能被修改,但是可以直接整体修改b1,如果b1是用const修饰就不能修改
b1={
	color:"grey",
	width:22,
	positon:"fixed"
}
THE END

发表回复