TypeScript基本语法
简介
ts 没有扩展 js 的内容,只是用来类型检测!
ts 最终是需要ts解析器(npm i -g typescript
)解析成js才能运行的,而这个解析器是用node写的,所有必须先装NodeJS。
详细教程可以直接去TypeScript入门教程查看。
- JavaScript 是一门解释型语言,没有编译阶段,是动态类型
- TypeScript 在运行前需要编译为 JavaScript,在编译阶段就会进行类型检查,是静态类型
- 它们都是弱类型
编译执行.ts
编译.ts
npm install -g typescript
就可以直接编译你的 .ts
文件到 .js
tsc xxx.ts
就会生成对饮的 .js
文件,然后直接使用node命令执行即可
node xxx.js
编译执行.ts
如果想一步到位实现编译+执行的话,使用 ts-node
npm install -g ts-node
ts-node xxx.ts
tsconfig.json
tsc --init
就会生成 tsconfig.json
,可以根据项目需要对其进行修改配置你想要的 ts 提示
类型
默认情况下 null
和 undefined
是所有类型的子类型
简单基础类型
let isDone:boolean = false // var isDone = false;
let decLiteral: number = 6 // var decLiteral = 6;
let myName: string = 'Jane' // var myName = 'Jane';
// 在不确定类型的时候尽量使用 unknown类型,不用any类型,unknown是安全的any类型
let a: unknown = 10
let anyThing: any = 'hello' // var anyThing = 'hello';
// void 类型表示没有返回值(没有return xxx)
function add(): void {
console.log('没有返回值')
}
联合类型
let myFavoriteNumber: string | number // var myFavoriteNumber;
数组
// 两种方式
let numberArray: number[] = [1, 2, 3] // var numberArray = [1, 2, 3];
let numberArray1: Array<number> = [4, 5, 6] // var numberArray1 = [4, 5, 6];
let arr: (string | number)[] = [1, 'a']
let arr: {name: string, age: number}[] = [
{name: 'Jane', age: 17},
{name: 'Jian', age: 20}
]
元组
// 元组类型允许表示一个已知元素数量和类型的数组,各元素的类型不必相同,超出部分采用的是联合类型
// 注意:类型顺序要对应,相当于plus版的数组了
let data: [number, string, boolean] = [2, '3', true] // var data = [2, '3', true];
枚举
enum Direction {
Up = 1, // 不赋值默认从0开始
Down,
Left,
Right
}
console.log(Direction.Down) // 2
console.log(Direction[1]) // Up
Direction.Up
的值为 1
, Down
为 2
, Left
为 3
, Right
为 4
(也可以指定为指定的字符串值)。
类型推论
// 定义变量时没有给变量指定类型但是赋值了,TypeScript 会依照类型推论的规则推断出一个类型,后面就不能将其赋值其他类型了
let myString = 'seven'
// 等价于
// let myString: string = 'seven'
// 定义变量时没有赋值,会推断为 any 类型
let myString;
myString = 'seven'
myString = 7
类型断言
告诉ts编译器:“我知道我在干什么,我能确定我现在的这个变量是什么类型”。ts 会假设你已经进行了类型的检查。
function getStrLen(str: number | string) {
// if(str.length) { 这里ts会因为不确定str是否一定是string而报错,告诉你 类型string|number上不存在length属性
if((<string>str).length) { // 类型断言str就是string类型(书写方式一)
return (str as string).length // 两种书写方式(书写方式二)
} else {
return str.toString().length
}
}
console.log(getStrLen('hello'))
类型别名
type Lady = {name: string, age: number} // 对象类型可以使用类型别名
let arr: Lady[] = [
{name: 'Jane', age: 17},
{name: 'Jian', age: 20}
]
接口(interface)
// 提供一种方式来定义某种结构,ts按照这种结构来检测数据(跟类型别名有点类型,不同的是接口必须赋值为对象)。
interface Options {
width: number,
height: number,
say(): string // 函数必须返回字符串,无返回值的类型是void
// color?: string, // 可选
// [attr: string]: number // key是string类型,value是number类型的属性都可以接受(可以接受多个)
}
function fn(opts: Options) {
// balabala
}
fn({
height: 200,
width: 100,
say() {
return 'Jane' // 必须返回 string 类型值
}
})
类实现接口
可以使用class MyClass implements MyInterface
让某个类实现某个接口
interface IFly{
fly(): string
}
// YouFly 类中必须有 IFly 接口中定义的 fly 方法,多个接口可以为 class xxx implements kkk,hhh{}
class YouFly implements IFly{
fly(): string {
return 'I can fly'
}
}
接口的继承
interface IFly{
fly(): string
}
interface ISwim{
swim(): string
}
interface IFlySwim extends IFly, ISwim{}
类
属性声明
// ts中的类,成员属性必须要先声明后使用,且在class内方法外声明
// public:公开的,类的内部和外部(实例中)都可以访问,属性和方法默认都是public
// protected:受保护的,在类的内部和他的子类中才能访问,实例中不能访问
// private:私有的,只能在该类的内部才能访问,子类和实例中都不能访问
// readonly:只读,实例不能对这个属性赋值
class Person {
username: string
private _age: number
protected job: string
constructor(name: string, old: number = 0, living: string) {
this.username = name
this._age = old
this.job = living
}
}
静态成员
class Person{
static job: string
constructor(j: string) {
// 此时job被static修饰,就是一个静态属性,不能通过this来访问了(this是实例对象)
// this.job = j
}
static sayJob() {
console.log('I have a job')
}
}
// static修饰的静态成员(静态属性和静态方法)只能通过类名来调用(不能通过实例对象来调用)
Person.job = 'coder' // 静态属性的修改
console.log(Person.job)
Person.sayJob()
存取器
// 存取器可以对设置的值进行限制
class Person {
private _age: number = 10
get age(): number {
return this._age
}
set age(age: number) {
if(age > 0 && age < 150) {
this._age = age
}
}
}
let p1: Person = new Person()
console.log(p1.age) // 10
p1.age = 300
console.log(p1.age) // 10
抽象类
抽象类不能用来创建对象,就是专门用来被继承的类
// 抽象类(只能被用来继承,不能被实例化new)
abstract class Girl{
// 抽象方法,抽象方法不能有具体的实现内容
abstract skill(): void
// 实例方法
gender() {
console.log('female')
}
}
// 继承自抽象类的类都必须有抽象类中的抽象方法
// 实例方法可以没有
// 实例化后的 const girl = new Girl() 可以直接调用抽象类的实例方法 girl.gender()
class Waiter extends Girl{
skill() {
console.log('serve')
}
}
class Teacher extends Girl{
skill() {
console.log('teach')
}
}
单例案例
class Mysql {
// 静态属性(不需要通过new出来的对象访问,直接通过类来访问)
public static instance
host: string
port: number
// private的构造函数,外部不能通过new来构造对象了
private constructor(host: string = '127.0.0.1', port: number = 3306) {
this.host = host
this.port = port
}
public static getInstance() {
// 单例,永远只有一个Mysql的实例
if(!Mysql.instance) {
// 可以通过内部来创建实例
Mysql.instance = new Mysql()
}
return Mysql.instance
}
query() {}
update() {}
}
let db = Mysql.getInstance()
db.query()
函数
函数类型
JavaScript 中有两种常见定义函数的方式——函数声明(Function Declaration)和函数表达式(Function Expression)。
一个函数有输入和输出,要在 TypeScript 中对其进行约束,需要把输入和输出都考虑到。
// Function Declaration
function sum(x: number, y: string): string {
return x + y
}
// Function Expression
// 这种情况 = 左侧是通过类型推断出来的
let sum = function (x: number, y: string): string {
return x + y
}
// Function Expression
// 这种情况 = 左侧是类型指定的
let sum: (x: number, y: string) => string = function (x: number, y: string): string {
return x + y
}
注意:TypeScript 中的 =>
用来表示函数定义,左边是输入类型,用括号括起来,右边是输出类型。跟 ES6 中的 =>
箭头函数还是不同的,不要混淆
可选参数
// 参数 y 可选
function sum(x: number, y?: string): string {
return x + y
}
默认参数
function sum(x: number, y: string = 'Jane'): string {
return x + y
}
剩余参数
function sum(x: number, ...rest: string[]): string[] {
return rest.map(i => i + x)
}
函数重载
JavaScript本身是个动态语言。 JavaScript里函数根据传入不同的参数而返回不同类型的数据是很常见的。这怎么在类型系统里表示呢?方法是为同一个函数提供多个函数类型定义来进行函数重载,编译器会根据这个列表去处理函数的调用,重载的函数在调用的时候会进行正确的类型检查。
function fn(x: number, y: number): number
function fn(x: string, y: string): string
function fn(x: string|number, y: string|number): string|number{
if(typeof x === 'number' && typeof y === 'number') {
return x + y
} else if(typeof x === 'string' && typeof y === 'string') {
return x + '_' + y
} else {
return '错误'
}
}
// 如果没有前两个重载这里不会报错,输出内容是“错误”
// 如果有了前两个重载这里就会报错,因为此时的参数不符合重载函数的任何一种传参数据类型
fn(10, 'kkk')
泛型
泛型函数
// 让函数的参数可以有多种类型,具体什么类型是我们使用的时候决定的
function fn<T>(val: T, count: number): T[]{
const arr: Array<T> = []
for(let i = 0; i < count; i++) {
arr.push(val)
}
return arr
}
// 在调用函数fn的时候, 参数是number类型,此时表示变量T赋值为number(因为调用的时候传入的是number)
// 也可以在调用的时候主动设置传入的值的类型
const arr1 = fn<number>(10, 3) // [10, 10, 10]
const arr2 = fn<string>('abc', 2) // ['abc', 'abc']
箭头函数泛型
<T>(contCn: T, contEn: T): T => {
return contCn || contEn
}
泛型接口
interface IFn<T> {
(x: T, y: T): number
}
// T就是number了
let fn: IFn<number> = function(x: number, y: number) {
return Number(x) + Number(y)
}
泛型类
interface Girl{
name: string
}
class SelectGirl<T extends Girl>{
constructor(private _girls: T[]) {}
getGirl(index: number): string {
return this._girls[index].name
}
}
new SelectGirl([{name: 'xxx'}, {name: 'kkk'}])
ts 中的 this
默认指向
js中函数的this指向
ts 中默认情况下函数中的 this
是 any
类型(ts 不能提示任何属性和方法,也就是类型系统没用了…所以我们一般都尽量不使用 any
类型)
let obj = {
a: 10,
fn() {
// this 是 any 类型
}
}
官方给出的解决方案是:给编译器(tsconfig.json
文件)设置 noImplicitThis: true
let obj = {
a: 10,
fn() {
// this 指向obj对象
}
}
设置this指向
我们也可以强行设置 this
的指向,比如:
let obj = {
a: 10,
fn() {
this // this指向obj对象
}
}
document.onclick = obj.fn // 期望的函数中的this指向document对象
ts 中函数的第一个参数是用来设置 this
类型约束的(这是一个假参数,没有实际的运行作用,只是用来给 ts 检测使用的)
let obj = {
a: 10,
fn(this: Document) {
this // this指向document对象
}
}
document.onclick = obj.fn
装饰器
尽可能不改变类结构的情况下,扩展其功能,可以被附加到类声明、属性、方法、参数或访问符上。
// {new()}表示是一个构造函数类型
function Age<T extends {new(...args: any[])}>(constructor: T): T {
class Person2 extends constructor{
age: number = 0
}
return Person2
}
// Age是一个装饰器函数,会自动调用,不需要()调用,调用的时候会传入下面对应的class的构造函数作为参数
@Age
class Person {
username: string = 'Jane'
}
let p = new Person()
console.log(p) // Person2 {username: 'Jane', age: 0}