由 Java 引谈到 TypeScript

文章目录
  1. 1. 简介 TypeScript
  2. 2. 上手 TypeScript
    1. 2.1. 基础类型
    2. 2.2. 变量声明
    3. 2.3. 接口
    4. 2.4.
    5. 2.5. 函数
    6. 2.6. 泛型
  3. 3. 参考文献

天下事有难易乎?为之,则难者亦易矣;不为,则易者亦难矣。

近来,接触到动态化 UI 框架应用到 Android 中的混合开发,使用的是微软的 TypeScript 语言。作为使用面向对象语言 Java 的 Android 开发者,会发现两者有很多相似的地方。借此机会,将 Java 和 TypeScript 作一个简单的比较,以归纳总结 TypeScript 简单的通常用法。

简介 TypeScript

Java 自不必多说,重点看一下 TypeScript,先看官网的简介

“TypeScript is a typed superset of JavaScript that compiles to plain JavaScript. Any browser. Any host. Any OS. Open source.”

大意即 TypeScript 是 JavaScript 类型的超集,其可以编译成纯的 JavaScript,能运行在任何浏览器上。TypeScript 是开源的,可以运行在任何服务器和系统上。

TypeScript 对于我们 Java 开发者来说,上手前端开发十分顺滑:

  • 代码的可读性和可维护性很好
  • 代码编译阶段就可以发现大部分的错误
  • 增强了编辑器和 IDE 的功能,如代码补全、跳转到定义等

上手 TypeScript

配备好 Node 环境后,TypeScript 安装如下:

1
npm install -g typescript

编译时执行,

1
tsc helloworld.ts

事实上,主流编辑器都支持 TypeScript,这里推荐 Visual Studio Code,其内置了 TypeScript 的支持,是一款开源、跨终端的轻量级编辑器。其他诸如 Sublime Text、Atom 等,看开发者的喜好了。

先从 Hello World 开始,新建一个 hello.ts 文件:

1
2
3
4
5
function sayHello(name: string) {
return 'Hello, ' + name;
}
let master = 'World';
console.log(sayHello(master));

保存后,终端进入文件所在目录,执行

1
tsc hello.ts

会生成一个编译好的 JavaScript 文件 hello.js:

1
2
3
4
5
function sayHello(name) {
return 'Hello, ' + name;
}
var master = 'World';
console.log(sayHello(master));

注意,TypeScript 只进行静态检查,若发现错误,编译期会报错,但还是会生成 JS 文件。不过,TypeScript 会警告代码不会按预期执行。

好,接下来,全面对照 Java,总结 TypeScript 简单的特性和常见使用。

基础类型

布尔值

Java中:

1
boolean isDone = false;

TypeScript中:

1
let isDone: boolean = false;

数字类型

Java中有 6 种基本数字类型,4 个整数类型:byte、short、int 和 long,2 个浮点类型:float 和 double:

1
float num = 1.0f;

TypeScript所有的数字都是浮点数,类型为 number:

1
let num: number = 1;

字符串

Java中,关键字 String 是大写:

1
String name = "zebron";

TypeScript中,关键字 string 是小写:

双引号形式,

1
let name: string = "zebron";

或单引号形式,

1
let name: string = 'zebron';

还可以使用模版字符串,定义多行文本和内嵌表达式,以反引号包围,同时,以${ expr }形式嵌入表达式:

1
let sentence: string = `Hello, my name is ${ name }`;

等价于

1
let sentence: string = "Hello, my name is " + name;

数组

Java中:

1
int[] list = {1, 2, 3};

TypeScript中:

1
let list: number[] = [1, 2, 3];

或使用数组泛型,

1
let list: Array<number> = [1, 2, 3];

元组

Java中没有内置的元组,使用的也不多。

TypeScript中,元组类型允许表示一个已知元素数量和类型的数组,且各个元素的类型不必相同,如:

1
2
3
4
5
6
// 声明一个有着 string 和 number 类型一对值的元组
let x: [string, number];
// 正确的初始化方式
x = ['zebron', 6];
// 错误的初始化方式
x = [6, 'zebron'];

枚举

JavaTypeScript枚举的用法几乎一致。

接下来,着重讲解几个 Java 中没有,且 TypeScript 中存在,甚至常用的类型

Any

编程阶段不清楚变量的类型时,如值来自动态的内容,可能是用户输入或者第三方的代码库时,要指定其类型,且不想让类型检查器对这些值进行检查,而是直接让它们通过编译阶段的检查,可以用any类型标记变量:

1
2
3
let foo: any = 6;
foo = "zebron"; // 正确
foo = false; // 正确

对现有代码进行改写时,any允许在编译时可选择地包含或移除类型检查。注意和 Object 的差异

Object 类型的变量只是允许给其赋任意值,但是不能使其可以调用任意的方法,即使是有!

1
2
3
4
5
6
let foo: any = 6;
foo.existedFunction(); // 正确,existedFunction() 也许是运行时存在的
foo.toFixed(); // 正确,toFixed() 存在的但编译器没有检查

let bar: Object = 6;
bar.toFixed(); // Object 里没有该方法时,这样调用或许是错误的

此外,只知道一部分数据的类型时,any 类型也有用,如:

1
2
let list: any[] = [6, true, "zebron"];
list[1] = 100;

注意,变量在声明时,未指定其类型,会被识别为任意值any类型,即

1
let value;

等价于

1
let value: any;

Undefined

undefined 有自己的类型叫做 undefined,其本身的类型用处不是很大:

1
let u: undefined = undefined;

默认情况下,undefined 是所有类型的子类型。

Never

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

  • never 类型可以是会抛出异常,或不会有返回值的表达式的返回值类型
  • 变量可能是 never 类型,其被永不为真的类型保护所约束时
  • never 类型是任何类型的子类型,可以赋值给任何类型
  • never 类型自身没有子类型,除了 never 其自身,且没有值可以赋值给 never 类型,any 也不可以赋值给 never
1
2
3
function error(message: string): never {
throw new Error(message);
}

类型断言

清楚地知道一个实体具有比其现有类型更确切的类型时,可以使用类型断言。

类型断言类似 Java 里的类型转换,但是不进行特殊的数据检查和解构,没有运行时的影响,只在编译阶段起作用。有两种形式:

“尖括号”式

1
2
let foo: any = "I am zebron";
let strLength: number = (<string>foo).length;

as 式

1
2
let foo: any = "I am zebron";
let strLength: number = (foo as string).length;

变量声明

letconst是 TypeScript 里变量的声明方式,const是对let的一个增强,能阻止对一个变量再次赋值,也就是const声明的变量有常量的意思。

关于以 let 代替 var 的原因,参见官方文档

constlet有着相同的作用域规则,但是正如前文所说,其引用的值是不可变的。

1
2
3
4
5
6
7
8
9
10
11
const programmer = {
name: "zebron"
}

// 错误
programmer = {
name: "soldier"
}

// 正确
programmer.name = "bob";

事实上,const变量的内部状态是可以修改的。

总结用法,除了计划去修改的变量,即不需要对一个变量写入时,都应该使用const

此外,关于解构,参见官方文档

接口

Java中:

1
2
3
4
5
6
7
8
9
10
interface ClockIf {
void setTime(Date d);
}

class Clock implements ClockIf {
@Override
public void setTime(Date d) {

}
}

TypeScript中:

1
2
3
4
5
6
7
8
9
interface ClockIf {
setTime(d: Date);
}

class Clock implements ClockIf {
setTime(d: Date) {

}
}

TypeScript 中,类实现接口、接口继承接口等,与 Java 中的差不多。现着重看看 TypeScript 中特殊的地方。

可选属性

接口里的属性并不是全都必需的,有些某些条件下存在,或者根本就不存在,可以给函数传入的参数对象中只有部分属性赋值。示例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
interface SquareConfig {
color?: string;
width?: number;
}

function createSquare(config: SquareConfig): {color: string; area: number} {
let newSquare = {color: "red", area: 66};

if (config.color) {
newSquare.color = config.color;
}
if (config.width) {
newSquare.area = config.width * config.width;
}

return newSquare;
}

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

这种接口,会在可选属性名字定义的后面加一个?符号,其好处:

  • 对可能存在的属性进行预定义
  • 引用不存在的属性时,可以捕获错误

只读属性

为了让某些对象属性只在对象刚刚创建的时候修改它的值,可以在属性名前加readonly指定其只读属性:

1
2
3
4
5
6
7
8
interface Rectangle {
readonly length: number;
readonly width: number;
}

// 初始赋值
let rec: Rectangle = {length: 10, width: 20};
rec.length = 5; // 错误

TypeScript 还具有个ReadonlyArray<T>的类型,与Array<T>类似,其只是把所有可变的方法去掉了,确保数组创建后再也不能被修改

1
2
3
4
5
6
7
let myArray: number[] = [1, 2, 3, 4];
let ra: ReadonlyArray<number> = myArray;
ra[0] = 6; // 错误
ra.push(5); // 错误
ra.length = 66; // 错误
myArray = ra; // 错误
myArray = ra as number[]; // 使用类型断言, 正确

那么问题来了,readonly 和 const 十分相似,该怎么用?作为变量的话使用 const,作为属性的话使用 readonly

TypeScript 里的类和 Java 中的类也大体相似,说说其不同点。

首先,上文接口中提到的,TypeScript 的类中也有readonly属性。

使用readonly将属性设置为可读的。注意,只读属性必须在声明时或构造器里被初始化。

存取器

TypeScript 里支持通过getters / setters有效地控制对对象成员的访问。不过,要注意的是,只带有get不带有set的存取器自动被推断为readonly

函数

几乎和 Java 中的方法特性类似。不过要注意的是,如前文提到的,TypeScript 里可以在参数名旁边使用?实现可选参数的功能。

泛型

几乎和 Java 中的泛型特性一致。

TypeScript 中,使用any类型会导致这个函数可以接受任何类型的 arg 参数,造成容易丢失一些信息:传入的类型与返回的类型应该是相同的。但是,使用any时,我们传入一个数字,任何类型的参数都可能会被返回。

所以,这里引入类型变量即泛型,解决了返回值的类型与传入参数的类型是相同的。

至此,对照 Java,引谈的 TypeScript 简单常用特性叙述到此结束,更多进阶细节及高级用法,参见官方文档

本人才疏学浅,如有疏漏错误之处,望读者中有识之士不吝赐教,谢谢。

1
Email: [email protected] / WeChat: Wolverine623

您也可以关注我个人的微信公众号码农六哥第一时间获得博客的更新通知,或后台留言与我交流

参考文献

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