ts - 基础

ts - 基础

TypeScript中文网 · TypeScript——JavaScript的超集

what

超集、扩展

扩展功能

  • 类型批注和编译时类型检查
  • 类型推断
  • 类型擦除
  • 接口
  • 枚举
  • Mixin
  • 泛型编程
  • 名字空间
  • 元组

类型

声明变量

var/let/const 变量名 [: 类型] [= 值];

类型推断

当类型没有给出时,TypeScript 编译器利用类型推断来推断类型。
如果由于缺乏声明而不能推断出类型,那么它的类型被视作默认的动态 any 类型。

var num = 2; // 类型推断为 number
console.log("num 变量的值为 "+num);
num = "12"; // 编译错误
console.log(num);

类型断言

类型断言可以用来手动指定一个值的类型,即允许变量从一种类型更改为另一种类型。

语法格式:

<类型>值值 as 类型

var str: string = '1'
var str2: number = <number> <any>str //str、str2 是 string 类型
console.log(str2, typeof str2)
var str3: number = str as any //str、str2 是 string 类型
console.log(str3, typeof str3)

联合类型

通过|将变量设置多种类型,赋值时可以根据设置的类型来赋值。

var val:string|number
// 联合类型作为函数参数
function disp(name:string|string[]) {
if(typeof name == "string") {
console.log(name)
} else {
var i;
for(i = 0;i<name.length;i++) {
console.log(name[i])
}
}
}
// 数组声明为联合类型
var arr:number[]|string[];

元组

数组中元素的数据类型都一般是相同的(any[] 类型的数组可以不同),如果存储的元素数据类型不同,则需要使用元组。

var mytuple = [10,"String",false];

访问控制修饰符

保护对类、变量、方法和构造方法的访问。

  • public(默认) : 公有,可以在任何地方被访问。
  • protected : 受保护,可以被其自身以及其子类访问。
  • private : 私有,只能被其定义所在的类访问。

只读属性

interface Point {
readonly x: number;
readonly y: number;
}

ReadonlyArray

let a: number[] = [1, 2, 3, 4];
let ro: ReadonlyArray<number> = a;
ro[0] = 12; // error!
ro.push(5); // error!
ro.length = 100; // error!
a = ro; // error!
// 就算把整个ReadonlyArray赋值到一个普通数组也是不可以的。 但是你可以用类型断言重写:
a = ro as number[];

readonly vs const

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

函数

返回值

function function_name()[:return_type] {
// 语句
return 'value'; // 返回值的类型需要与函数定义的返回类型(return_type)一致。
}

可选参数

  • 定义了参数,必须传入参数,除非将这些参数设置为可选,可选参数使用问号标识 ?
  • 可选参数必须跟在必需参数后面。
function buildName(firstName: string, lastName: string) {
return firstName + " " + lastName;
}
let result1 = buildName("Bob"); // 错误,缺少参数
let result2 = buildName("Bob", "Adams", "Sr."); // 错误,参数太多了
let result3 = buildName("Bob", "Adams"); // 正确
// 可选参数
function buildName(firstName: string, lastName?: string) {
if (lastName)
return firstName + " " + lastName;
else
return firstName;
}
let result1 = buildName("Bob"); // 正确
let result2 = buildName("Bob", "Adams", "Sr."); // 错误,参数太多了
let result3 = buildName("Bob", "Adams"); // 正确

剩余参数

不知道要向函数传入多少个参数,剩余参数语法允许我们将一个不确定数量的参数作为一个数组传入。

函数的最后一个命名参数 restOfName ... 为前缀 ,它将成为一个由剩余参数组成的数组,索引值从0(包括)到 restOfName.length(不包括)

function buildName(firstName: string, ...restOfName: string[]) {
return firstName + " " + restOfName.join(" ");
}
let employeeName = buildName("Joseph", "Samuel", "Lucas", "MacKinzie");

泛型

  • 泛型的语法是 <> 里写类型参数,一般可以用 T 来表示。
    • 泛型中的 T 就像一个占位符、或者说一个变量,在使用的时候可以把定义的类型像参数一样传入,它可以原封不动地输出。
  • 输入和输出的类型统一,且可以输入输出任何类型。
function print<T>(arg:T):T {
console.log(arg)
return arg
}

泛型约束

操作某类型的一组值,编译器并不能证明每种类型具有什么样的属性报错了。

function loggingIdentity<T>(arg: T): T {
console.log(arg.length); // Error: T doesn't have .length
return arg;
}

需要列出对于T的约束要求

interface Lengthwise {
length: number;
}
function loggingIdentity<T extends Lengthwise>(arg: T): T {
console.log(arg.length); // Now we know it has a .length property, so no more error
return arg;
}

枚举

数字枚举

枚举成员值

  • 每个枚举成员都有一个 name 和一个 value。数字枚举成员值的默认类型是 number 类型。也就是说,每个成员的值都是一个数字
  • 除了让 TypeScript 为我们指定枚举成员的值之外,我们还可以手动赋值
  • 通过等号的显式赋值称为 initializer。如果枚举中某个成员的值使用显式方式赋值,但后续成员未显示赋值, TypeScript 会基于当前成员的值加 1 作为后续成员的值

枚举成员名称的转换

  • TypeScript 手册使用以大写字母开头的驼峰式名称。

反向映射

数字枚举成员还具有了反向映射,从枚举值到枚举名字

enum Direction {
Up,
Down,
Left,
Right
}
console.log(Direction); // {0: 'Up', 1: 'Down', 2: 'Left', 3: 'Right', Up: 0, Down: 1, Left: 2, Right: 3}
enum DirectionInit {
Up = 1,
Down,
Left,
Right
}
console.log(DirectionInit); // {1: 'Up', 2: 'Down', 3: 'Left', 4: 'Right', Up: 1, Down: 2, Left: 3, Right: 4}

字符串枚举

  • 字符串枚举没有自增长的行为
enum Direction {
Up = 'UP',
Down = 'DOWN',
Left = 'LEFT',
Right = 'RIGHT'
}
console.log(Direction) // {Up: 'UP', Down: 'DOWN', Left: 'LEFT', Right: 'RIGHT'}

const枚举

  • 避免在额外生成的代码上的开销和额外的非直接的对枚举成员的访问
const enum Directions {
Up,
Down,
Left,
Right
}
console.log(Directions.Up) // 0
console.log(Directions) // 'const' enums can only be used in property or index access expressions or the right hand side of an import declaration or export assignment or type query.

外部枚举

  • 外部枚举用来描述已经存在的枚举类型的形状。
declare enum Enum {
A = 1,
B,
C = 2
}
  • 外部枚举和非外部枚举之间有一个重要的区别,在正常的枚举里,没有初始化方法的成员被当成常数成员。 对于非常数的外部枚举而言,没有初始化方法时被当做需要经过计算的。

接口

接口是一系列抽象方法的声明,是一些方法特征的集合,这些方法都应该是抽象的,需要由具体的类去实现,然后第三方就可以通过这组抽象方法调用,让具体的类执行具体的方法。

interface interface_name {
}

联合类型接口

interface RunOptions {
program:string;
commandline:string[]|string|(()=>string);
}
// commandline 是字符串
var options:RunOptions = {program:"test1",commandline:"Hello"};
console.log(options.commandline)
// commandline 是字符串数组
options = {program:"test1",commandline:["Hello","World"]};
console.log(options.commandline[0]);
console.log(options.commandline[1]);
// commandline 是一个函数表达式
options = {program:"test1",commandline:()=>{return "**Hello World**";}};
var fn:any = options.commandline;
console.log(fn());

接口数组

interface namelist {
[index:number]:string
}
// 类型一致,正确
var list2:namelist = ["Google","Runoob","Taobao"]
// 错误元素 1 不是 string 类型
// var list2:namelist = ["Runoob",1,"Taobao"]

接口继承

  • 通过其他接口来扩展自己
  • 允许接口继承多个接口。
  • 继承使用关键字 extends。
  • 继承的各个接口使用逗号 , 分隔。
Child_interface_name extends super_interface1_name[, super_interface2_name,…,super_interfaceN_name]
// eg:
interface Person {
age:number
}
interface Musician extends Person {
instrument:string
}
var drummer = <Musician>{};
drummer.age = 27
drummer.instrument = "Drums"
console.log("年龄: "+drummer.age)
console.log("喜欢的乐器: "+drummer.instrument)

类和接口

类可以实现接口,使用关键字 implements,并将 interest 字段作为类的属性使用。

interface ILoan {
interest:number
}
class AgriLoan implements ILoan {
interest:number
rebate:number
constructor(interest:number,rebate:number) {
this.interest = interest
this.rebate = rebate
}
}
var obj = new AgriLoan(10,1)
console.log("利润为 : "+obj.interest+",抽成为 : "+obj.rebate )

类型别名

  • 类型别名会给一个类型起个新名字。 类型别名有时和接口很像,但是可以作用于原始值,联合类型,元组以及其它任何你需要手写的类型。
  • 不会新建一个类型 - 它创建了一个新 名字来引用那个类型。 给原始类型起别名通常没什么用,尽管可以做为文档的一种形式使用。
type Name = string;
type NameResolver = () => string;
type NameOrResolver = Name | NameResolver;
function getName(n: NameOrResolver): Name {
if (typeof n === 'string') {
return n;
}
else {
return n();
}
}

interface vs type

相同点

  1. 定义一个对象或函数
type addType = (num1:number,num2:number) => number
interface addType {
(num1:number,num2:number):number
}
// 这两种写法都可以定义函数类型
const add:addType = (num1, num2) => {
return num1 + num2
}
  1. 允许继承(extends
// interface 继承 interface
interface Person {
name: string
}
interface Student extends Person {
grade: number
}
// type 继承 type
type Person = {
name: string
}
type Student = Person & { grade: number } // 用交叉类型
// interface 继承 type
type Person = {
name: string
}
interface Student extends Person {
grade: number
}
// type 继承 interface
interface Person {
name: string
}
type Student = Person & { grade: number } // 用交叉类型

不同点

  • type 可以,interface 不行

声明基本类型、联合类型、交叉类型、元组

type Name = string // 基本类型
type arrItem = number | string // 联合类型
const arr: arrItem[] = [1,'2', 3]
type Person = {
name: Name
}
type Student = Person & { grade: number } // 交叉类型
type Teacher = Person & { major: string }
type StudentAndTeacherList = [Student, Teacher] // 元组类型
const list:StudentAndTeacherList = [
{ name: 'lin', grade: 100 },
{ name: 'liu', major: 'Chinese' }
]
  • interface可以,type 不行

合并重复声明

interface Person {
name: string
}
interface Person { // 重复声明 interface,就合并了
age: number
}
const person: Person = {
name: 'lin',
age: 18
}
// 重复声明 type 报错
type Person = {
name: string
}
type Person = { // Duplicate identifier 'Person'
age: number
}

命名空间

  • 解决重名问题。
  • 命名空间定义了标识符的可见范围,一个标识符可在多个名字空间中定义,它在不同名字空间中的含义是互不相干的。这样,在一个新的名字空间中可定义任何标识符,它们不会与任何已有的标识符发生冲突,因为已有的定义都处于其他名字空间中。

语法格式

namespace SomeNameSpaceName {
export interface ISomeInterfaceName { }
export class SomeClassName { }
}
// 以上定义了一个命名空间 SomeNameSpaceName,如果我们需要在外部可以调用 SomeNameSpaceName 中的类和接口,则需要在类和接口添加 export 关键字。
// 要在另外一个命名空间调用语法格式为:
SomeNameSpaceName.SomeClassName;

文件引用

如果一个命名空间在一个单独的 TypeScript 文件中,则应使用三斜杠 /// 引用它,语法格式如下:

/// <reference path = "SomeFileName.ts" />

实例

// IShape.ts
namespace Drawing {
export interface IShape {
draw();
}
}
// Circle.ts
/// <reference path = "IShape.ts" />
namespace Drawing {
export class Circle implements IShape {
public draw() {
console.log("Circle is drawn");
}
}
}
// Triangle.ts
/// <reference path = "IShape.ts" />
namespace Drawing {
export class Triangle implements IShape {
public draw() {
console.log("Triangle is drawn");
}
}
}
// TestShape.ts
/// <reference path = "IShape.ts" />
/// <reference path = "Circle.ts" />
/// <reference path = "Triangle.ts" />
function drawAllShapes(shape:Drawing.IShape) {
shape.draw();
}
drawAllShapes(new Drawing.Circle()); // Circle is drawn
drawAllShapes(new Drawing.Triangle()); // Triangle is drawn

嵌套命名空间

将命名空间定义在另外一个命名空间里头。

namespace namespace_name1 {
export namespace namespace_name2 {
export class class_name { }
}
}

成员的访问使用点号 . 来实现

new namespace_name1.namespace_name2.class_name()
posted @   zc-lee  阅读(39)  评论(0编辑  收藏  举报
历史上的今天:
2022-03-31 vscode - plugs - koroFileHeader
点击右上角即可分享
微信分享提示