对于TS的总结:也是个人的学习体会,可能会有理解偏差.
目录
什么是TS(TypeScript)?
1、定义:
TypeScript 是一种由微软开发的自由和开源的编程语言。它是 JavaScript 的一个超集,TypeScript 在 JavaScript 的基础上添加了可选的静态类型和基于类的面向对象编程。
2、始于js终于js:
前端中使用js编译,最终也会编译为js
其实TypeScript就是相当于JavaScript的增强版,但是最后运行时还要编译成JavaScript。TypeScript最大的目的是让程序员更具创造性,提高生产力,它将极大增强JavaScript编写应用的开发和调试环节,让JavaScript能够方便用于编写大型应用和进行多人协作。
3、官方网站
TS官网:TypeScript中文网 · TypeScript——JavaScript的超集
jsx也是TS的一个分支
4、TS与JS比较
- TypeScript是一个应用程序级的JavaScript开发语言。(这也表示TypeScript比较牛逼,可以开发大型应用,或者说更适合开发大型应用)
- TypeScript是JavaScript的超集,可以编译成纯JavaScript。这个和我们CSS离的Less或者Sass是很像的,我们用更好的代码编写方式来进行编写,最后还是有好生成原生的JavaScript语言。
- TypeScript跨浏览器、跨操作系统、跨主机、且开源。由于最后他编译成了JavaScript所以只要能运行JS的地方,都可以运行我们写的程序,设置在node.js里。
- TypeScript始于JavaScript,终于JavaScript。遵循JavaScript的语法和语义,所以对于我们前端从业者来说,学习前来得心应手,并没有太大的难度。
- TypeScript可以重用JavaScript代码,调用流行的JavaScript库。
- TypeScript提供了类、模块和接口,更易于构建组件和维护。
以上文字来自个人网站[技术胖]
网站:技术胖-TypeScript免费视频教程 ,Deno前置知识 (共15集)
TypeScript开始
一、安装TS
- 安装node.js 网址:Node.js
- 安装TS包
-
npm install -g typescript
二、编译代码
- 使用了
.ts
扩展名 - tsc greeter.ts运行一段TS代码,输出结果为一个
greeter.js
文件,它包含了和输入文件中相同的JavsScript代码。(始于js终于js,TS在前端中不是脚本语言而是文档,JS是一种编程的脚本语言)
三、TS高级功能
1、静态类型(基础静态类型与对象静态类型)
TypeScript
最大的一个特点就是变量是强类型的,也就是说,在声明变量的时候,我们必须给他一个类型。- 基础静态类型与对象静态类型,可以参考基础类型和引用类型
const age: number = 9
这里声明一个变量,冒号后面是赋的一个基础类型
对象静态类型:对象类型,数组类型,类,函数,....
TypeScript中的数据类型有:
- Undefined :未定义的类型;
- Number:数值类型;
- string : 字符串类型;
- Boolean: 布尔类型;
- enum:枚举类型;
- any : 任意类型;
- void:空类型;
- Array : 数组类型;
- Tuple : 元祖类型;
- Null :空类型;
- class {}: 类类型
- function:函数类型
- never:永远无法执行完(函数返回值)
enum:枚举类型
枚举类型是ts特有的类型,js内没有
如果没有给nan,nv,yao赋值,默认情况下是从0开始往后排,
如果给第一个赋值为1,后面未赋,就会 从1开始往后排
反查:与数组类似,可以通过下标查询 REN[0] => man(未赋值时)
//变量的结果是固定的几个数据时,使用枚举类型最合适
//想给这些枚举赋值,可以直接使用=,来进行赋值
enum REN{
nan = '男',
nv = '女',
zhong= '中性'
}
console.log(REN.zhong) //返回了中性 这个字
Tuple : 元祖类型
元组类型允许表示一个已知元素数量和类型的数组,各元素的类型不必相同。 比如,你可以定义一对值分别为 string
和number
类型的元组。
// Declare a tuple type
let x: [string, number];
// Initialize it
x = ['hello', 10]; // OK
// Initialize it incorrectly
x = [10, 'hello']; // Error
类类型
class Person{}
const Son : Person = new Person()
函数类型:string代表的是返回值的类型
const function : () => string = () => {return '123456'}
官网有更加详细的说明:可以直接移步
2、类型注解与类型推断
类型注解:就是对声明的变量、函数、数组等定义一个类型,冒号后面的就是类型注解。
类型推断:当你对部分变量等进行了类型注解之后,如果由它变来的变量等是一个固定的类型,那么系统就可以自行推断出类型。
3、接口(interface)
接口与类型别名的区别
类型别名:字面意就是给类型起一个别名。
type age = number
接口:TypeScript的核心原则之一是对值所具有的结构进行类型检查。 它有时被称做“鸭式辨型法”或“结构性子类型化”。 在TypeScript里,接口的作用就是为这些类型命名和为你的代码或第三方代码定义契约。
接口要求:
- 必须代表的是一个对象
接口特点:
- 接口里的属性不全都是必需的。(
:
号前加一个?,表示可以是定义的类型,也可以没有
) - 只读属性:一些对象属性只能在对象刚刚创建的时候修改其值。 你可以在属性名前用
readonly
来指定只读属性: - 额外检查属性
- 函数类型:为了使用接口表示函数类型,我们需要给接口定义一个调用签名。 它就像是一个只有参数列表和返回值类型的函数定义。参数列表里的每个参数都需要名字和类型。
- 可索引类型:与使用接口描述函数类型差不多,我们也可以描述那些能够“通过索引得到”的类型
- 类类型:实现接口与C#或Java里接口的基本作用一样,TypeScript也能够用它来明确的强制一个类去符合某种契约。
- 继承接口:和类一样,接口也可以相互继承。 这让我们能够从一个接口里复制成员到另一个接口里,可以更灵活地将接口分割到可重用的模块里。
- 混合类型:JavaScript其动态灵活的特点,有时你会希望一个对象可以同时具有上面提到的多种类型。
具体的例子请移步:接口 · TypeScript中文网 · TypeScript——JavaScript的超集
interface Girl {
name: string;
age: number;
bust: number;
waistline?: number;
[propname: string]: any;
}
//这个的意思是,属性的名字是字符串类型,属性的值可以是任何类型。
混合类型:对基础类型的混合,经常使用的就是或与非(&与|)
string[ ]、number[ ]、string | number、[string, number,boolean]等
4、类(class)
4.1官网给出的:
使用TypeScript,我们允许开发者现在就使用类的特性,并且编译后的JavaScript可以在所有主流浏览器和平台上运行,而不需要等到下个JavaScript版本。
class Greeter {
greeting: string;
constructor(message: string) {
this.greeting = message;
}
greet() {
return "Hello, " + this.greeting;
}
}
let greeter = new Greeter("world");
//我们声明一个 Greeter类。这个类有3个成员:
一个叫做 greeting的属性,一个构造函数和一个 greet方法。
4.2继承:
在TypeScript里,我们可以使用常用的面向对象模式。 基于类的程序设计中一种最基本的模式是允许使用继承来扩展现有的类。
class Animal {
move(distanceInMeters: number = 0) {
console.log(`Animal moved ${distanceInMeters}m.`);
}
}
class Dog extends Animal {
bark() {
console.log('Woof! Woof!');
}
}
const dog = new Dog();
dog.bark();
dog.move(10);
dog.bark();
//Dog是一个 派生类,它派生自 Animal 基类,通过 extends关键字。
派生类通常被称作子类,基类通常被称作超类。
//因为 Dog继承了 Animal的功能,因此我们可以
创建一个 Dog的实例,它能够 bark()和 move()。
4.3公共,私有与受保护的修饰符
4.31默认为 public:(翻译:公共的)
在TypeScript里,class成员都默认为 public
。
class Animal {
public name: string;
public constructor(theName: string) { this.name = theName; }
public move(distanceInMeters: number) {
console.log(`${this.name} moved ${distanceInMeters}m.`);
}
}
4.32理解 private:(翻译:私有的)
当成员被标记成 private
时,它就不能在声明它的类的外部访问。
class Animal {
private name: string;
constructor(theName: string) { this.name = theName; }
}
new Animal("Cat").name; // 错误: 'name' 是私有的.
4.33理解 protected:(翻译:受保护的)
protected
修饰符与 private
修饰符的行为很相似,但有一点不同, protected
成员在派生类中仍然可以访问。
派生类,它派生自 Animal 基类,通过 extends关键字。 派生类通常被称作子类,基类通常被称作超类。(可以简单的理解为子类可以访问父类)
getter setter使用
class Girl {
constructor(private _age:number){}
get age(){
return this._age-10
}
set age(age:number){
this._age=age
}
}
const xiaoming = new Girl(28)
xiaomimg.age=25
console.log(xiaoming.age) //15
//getter setter属性进行改变
//作用是,用于一些封装
4.4readonly修饰符(只读)
你可以使用 readonly
关键字将属性设置为只读的。 只读属性必须在声明时或构造函数里被初始化。
4.5存取器
TypeScript支持通过getters/setters来截取对对象成员的访问。 它能帮助你有效的控制对对象成员的访问。
4.6静态属性
可以创建类的静态成员,这些属性存在于类本身上面而不是类的实例上。
4.7抽象类
抽象类做为其它派生类的基类使用。 它们一般不会直接被实例化。 不同于接口,抽象类可以包含成员的实现细节。 abstract
关键字是用于定义抽象类和在抽象类内部定义抽象方法。
简单的来讲:就是继承类(子类),里面必须使用抽象类的抽象方法,抽象方法的返回属性是空类型,子类里面使用时.可以自行定义
abstract class Animal {
abstract makeSound(): void;
move(): void {
console.log('roaming the earch...');
}
}
4.8把类当做接口使用
class Point {
x: number;
y: number;
}
interface Point3d extends Point {
z: number;
}
let point3d: Point3d = {x: 1, y: 2, z: 3};
重点讲一下extents与implements区别
extends是继承父类,只要那个类不是声明为final或者那个类定义为abstract的就能继承,JAVA中不支持多重 继承,但是可以用接口来实现,这样就要用到implements,继承只能继承一个类,但implements可以实现多个接口,用逗号分开就行了
class A extends B implements C,D,E
// 一个类通过关键字implements声明自己使用一个或者多个接口。在类的声明中,通过关键字extends来创建一个类的子类。
class 子类名 extends 父类名 implenments 接口名
{...
}
5、tsconfig.json文件
tsconfig.json文件作用:ts的配置文件,最主要的功能就是如何把ts文件转换为js文件
生成tsconfig.json文件 tsc --init
5.1tsconfig.json文件内部关键词:
(1)使用 include(只编译)配置
exclude(不包括) 、 files 配置,这两种基本相同
{
"include":["*.ts"],
"compilerOptions": {
//any something
//........
}
}
//编译所有以ts结尾的文件
(2)compilerOptions
配置项
removeComments:删除注释
strict
属性如果设置为true
,就代表我们的编译和书写规范
noImplicitAny
属性的作用是,允许你的注解类型 any 不用特意表明(可以理解为严格设置)
[意思就是值就算是 any(任意值),你也要进行类型注释。]
strictNullChecks
设置为false
,它的意思就是,不强制检查 NULL 类型。
rootDir与outDir,当js与ts文件混合的时候,通过rootDir
来指定ts
文件编译后的位置(出口文件)
outDir表示编译前的位置(入口文件)
"target":'es5' 这一项默认是开启的,你必须要保证它的开启,才能转换成功
"allowJs":true 这个配置项的意思是联通
noUnusedLocals:true
,开启后,当代码里面有声明了变量没有赋值时,程序会直接提示不能这样编写代码,有没有使用的变量。
6、联合类型与类型守护
6.1联合类型:
联合类型,可以认为一个变量可能有两种或两种以上的类型。
关键符号是|
(竖线)
类型保护:类型断言就是通过断言的方式确定传递过来的准确值
简单来讲就是:把联合类型组合的不同类型,全部用判断出来
常用的语法
- 类型断言 as语法
- 类型保护 in语法
- 类型保护 typeof语法
interface Waiter {
anjiao: boolean;
say: () => {};
}
interface Teacher {
anjiao: boolean;
skill: () => {};
}
//使用as语法
function judgeWho(animal: Waiter | Teacher) {
if (animal.anjiao) {
(animal as Teacher).skill();
}else{
(animal as Waiter).say();
}
}
//使用in语法
function judgeWhoTwo(animal: Waiter | Teacher) {
if ("skill" in animal) {
animal.skill();
} else {
animal.say();
}
}
//使用typeof语法
function add(first: string | number, second: string | number) {
if (typeof first === "string" || typeof second === "string") {
return `${first}${second}`;
}
return first + second;
}
7、泛型
7.1定义
组件不仅能够支持当前的数据类型,同时也能支持未来的数据类型,这在创建大型系统时为你提供了十分灵活的功能。
在像C#和Java这样的语言中,可以使用泛型
来创建可重用的组件,一个组件可以支持多种类型的数据。 这样用户就可以以自己的数据类型来使用组件。
泛型:泛指的类型 标识符: < >
常用的 <string> 、Array<string>与string[ ]相同
7.2函数中的泛型
常用T在泛型中指代类型
function join<T>(first: T, second: T) {
return `${first}${second}`;
}
join < string > ("xiaoMing", "xiaoHong");
//这里T指定的就是string
//通过调用来对泛型进行赋值 (未来的数据类型)
7.3类中的泛型
类中的泛型使用与函数中类使用差不多,重点是类有继承属性,泛型也可以继承
7.31泛型T继承了接口Student
interface Student {
name: string;
}
class SelectStudent<T extends Student> {
constructor(private students: T[]) {}
getStudent(index: number): string {
return this.students[index].name;
}
}
const selectStudent = new SelectStudent([
{ name: "小明" },
{ name: "小红" },
{ name: "小黄" },
]);
console.log(selectStudent.getStudent(1)); //小明
//T继承自Student接口
//T[] 就是 数组里面是对象,对象里面的name是string
//使用new 一个实例对象,实例原型上有constructor()和getStudent()
7.32泛型的约束
< T extents string | number >
这里就表示泛型T的类型要么是string,要么是number(对T进行了一个约束效果)
8、命名空间
8.1介绍
如何在TypeScript里使用命名空间(之前叫做“内部模块”)来组织你的代码。 就像我们在术语说明里提到的那样,“内部模块”现在叫做“命名空间”。 另外,任何使用 module
关键字来声明一个内部模块的地方都应该使用namespace
关键字来替换。 这就避免了让新的使用者被相似的名称所迷惑。
简单的来讲:namespace关键字就是和JS里面的内部模块一样,主要的作用就是防止全局污染.
使用namespace Home { }包裹起来,形成一个单独的模块,需要在外部使用的进行暴露
namespace Home {
class Header {
constructor() {
const elem = document.createElement("div");
elem.innerText = "This is Header";
document.body.appendChild(elem);
}
}
class Content {
constructor() {
const elem = document.createElement("div");
elem.innerText = "This is Content";
document.body.appendChild(elem);
}
}
class Footer {
constructor() {
const elem = document.createElement("div");
elem.innerText = "This is Footer";
document.body.appendChild(elem);
}
}
export class Page {
constructor() {
new Header();
new Content();
new Footer();
}
}
}
//这里的Home就形成命名空间,只有暴露的Page可以查到
具体请看:命名空间和模块 · TypeScript中文网 · TypeScript——JavaScript的超集
9、import语法引入
注意点:
export class Header {
constructor() {
const elem = document.createElement("div");
elem.innerText = "This is Header";
document.body.appendChild(elem);
}
}
export class Content {
constructor() {
const elem = document.createElement("div");
elem.innerText = "This is Content";
document.body.appendChild(elem);
}
}
export class Footer {
constructor() {
const elem = document.createElement("div");
elem.innerText = "This is Footer";
document.body.appendChild(elem);
}
}
//上面的代码为components文件内
import { Header, Content, Footer } from "./components";
export class Page {
constructor() {
new Header();
new Content();
new Footer();
}
}
编译后的代码:编译为一个js文件(page.js),都是define
开头的(这是 amd 规范的代码,不能直接在浏览器中运行,可以在 Node 中直接运行),这种代码在浏览器中是没办法被直接运行的,需要其他库(require.js
)的支持。
已经加入了require.js
这个库,所以现在可以直接在代码中使用require
了。具体代码如下:
Require.js 的 CDN 地址: https://cdnjs.cloudflare.com/ajax/libs/require.js/2.3.6/require.js
10、打包工具Parcel和webpack
打包工具的内容:代码运行不是我们所写的代码,而是已经打包好的代码,前端常用打包成为html、css、js,依托浏览器进行解析展现.
(特殊的React-Native,全部使用js代码编辑,使用的Metro工具是打包成为java或者oc)
上面的打包自己操作,太麻烦了
现在市面上有Parcel和webpack两种打包工具,能够让我们更快的打包
Vue使用的就是webpack打包的,vue3已经开始使用Vite工具
Parcel的优势