在过去的几年里,很少有技术能像TypeScript那样产生影响。
让我补充一点有利于TypeScript的社会证明。
在 "The State of JavaScript 2018 "的调查中,近50%的受访者表示他们使用过TypeScript,并会再次使用它。超过30%的人表示他们想学习它。这是对它感兴趣的人的巨大比例。
TypeScript是由微软建立的,微软在创建编程语言方面并不陌生,其创建者之一是Anders Hejlsberg,一位丹麦软件工程师,他因Turbo Pascal(❤️)和Delphi而闻名。我把心放在Turbo Pascal旁边,因为Pascal是我的第一种编程语言,我们在学校使用Turbo Pascal。
它是一种开源语言,在github.com/Microsoft/T…,是公开开发的。
Angular是关于TypeScript的,Vue.js据说要用TypeScript做第三版。Node.js的创造者Ryan Dahl也说过关于它的好话。
我认为这些事情有助于你把TypeScript放在正确的位置。它不只是一个下个月就会消亡的随机的JavaScript味道,它肯定会留下来。而随着事情的发展,这意味着你很可能需要在未来的项目中,或者在你的下一份工作中使用它。也许它也会帮助你找到工作,所以让我们直接进入它。
编写和编译你的第一个TypeScript文件
开始使用TypeScript很容易。如果你曾经写过一行JavaScript,你就已经写过TypeScript代码了!
我的这个奇怪的说法是TypeScript成功的原因之一:它是JavaScript的严格超集。
这有点像SCSS对CSS的作用。
特别是,它是ECMAScript 2015(也被称为ES6)的超集。这意味着,任何有效的JavaScript也是有效的TypeScript。
TypeScript的许多功能都等同于JavaScript的功能。例如,变量,模块系统,迭代器等等。
所以,没有必要写你的第一个TypeScript文件,因为你已经在不知道的情况下写了,但让我们通过明确地制作一个TypeScript文件来做一个小小的 "hello world!",并将其编译为JavaScript。
运行npm install -g typescript ,全局安装TypeScript编译器,你可以使用tsc 命令。
建立一个新的文件夹,并创建一个app.ts 文件。ts 是TypeScript文件的扩展名。
编写这第一个程序。
const greet = () => {
console.log('Hello world!')
}
greet()
这只是普通的JavaScript,但存储在一个.ts 文件中。
现在用tsc app.ts 来编译这个程序。结果将是一个新的JavaScript文件。app.js,有这样的内容。
var greet = function () {
console.log('Hello world!');
};
greet();
TypeScript代码已经被编译为JavaScript。这个JavaScript代码发生了一些变化,例如你可以注意到它增加了分号,并且使用了var ,而不是const ,还使用了一个普通函数,而不是箭头函数。
它看起来像旧的JavaScript,对吗?这是因为TypeScript默认编译到ES5,因为这是ECMAScript的版本,几乎可以保证在所有的现代浏览器中得到支持。你可以把编译目标改成其他版本,例如,要把ES2018作为目标,使用tsc app.ts --target ES2018 。
const greet = () => {
console.log('Hello world!');
};
greet();
看,这里除了增加了分号外,几乎没有对我们原来的.ts 文件做任何改动。
有一个非常方便的在线游乐场,可以让你玩玩TypeScript到JavaScript的编译,在www.typescriptlang.org/play/。
类型化
类型化是TypeScript的关键特征。
到目前为止,我们编译了一个.ts 文件,但我们只是编译了普通的JavaScript。
你看到了TypeScript的第一个特点:你可以使用现代的JavaScript并将其编译为ES5(或更高),有点像Babel做的那样。
我们并没有使用TypeScript的任何功能。
TypeScript提供的最重要的功能是类型系统:静态类型、接口、类型推理、枚举、混合类型、泛型、联合/交叉类型、访问修改器、空值检查。
如果你曾经使用过类型化的语言,如Go或C,你已经知道这是如何工作的。如果没有,而你只用Python或Ruby这样的动态语言编程,这对你来说都是新的,但不要担心。
类型系统允许你,例如,为你的变量、函数参数和函数返回类型添加类型,给你的程序提供一个更严格的结构。
其优点是有更好的工具:编译器(和VS Code等编辑器)在开发过程中可以帮助你很多,在你写代码时指出错误。如果你没有类型,就不可能发现这些错误。另外,由于代码更加明确,团队合作也变得更加容易。
当然,我们编译出来的JavaScript代码是没有类型的:它们在编译阶段就丢失了,但编译器会指出它发现的任何错误。
下面是你如何在TypeScript中定义字符串变量。
const greeting : string = "hello!"
类型推理让我们在像这样明显的情况下避免写出类型。
const greeting = "hello!"
类型是由TS决定的。
这就是一个函数如何接受一个特定类型的参数。
const multiply = (a: number, b: number) => {
return a * b
}
如果你传递一个字符串给multiply() ,编译器会给你一个错误。
下面是函数如何声明它们的返回值。
const multiply = (a: number, b: number): number => {
return a * b
}
有效的类型是
numberstringbooleanenumvoidnullundefinedanyneverArraytuple
any 是一个万能的类型,正如它的名字所说,它可以识别任何类型。
类
ES2015/ES6为JavaScript添加了类,作为原型继承的一个简单的语法糖。
不管你喜不喜欢,JavaScript仍然在使用原型继承,它有所有独特的功能和怪癖。
TypeScript的类与JavaScript的类有一点不同。原因是TypeScript在JavaScript有类之前就引入了类(它们是在ES2015/ES6中引入的)。
就像在JavaScript中,你以这种方式声明类。
这是你定义类字段的方式。
class Car {
color: string
}
所有字段默认都是公共的。你可以将一个字段设置为私有或保护。
class Car {
public color: string
private name: string
protected brand: string
}
像其他编程语言一样,私有字段只能在声明它们的类中被访问。受保护的字段也只能被派生类访问。
你也可以声明静态字段,它是类的字段而不是对象的字段。
class Car {
static numberOfWheels = 4
}
你可以用一个构造函数来初始化字段。
class Car {
color: string
constructor(theColor: string) {
this.color = theColor
}
}
这种简略的语法使它更简单。
class Car {
constructor(public color: string) {}
printColor() {
alert(this.color)
}
}
(new Car('red')).printColor()
注意我们是如何使用this.x 来引用类字段的。
一个字段也可以是只读的。
class Car {
readonly color: string
}
而在这种情况下,它的值只能在构造函数中设置。
类有方法。
class Car {
color: string
constructor(public color: string) {
this.color = color
}
drive() {
console.log('You are driving the car')
}
}
像在普通的JavaScript中一样,你从这些类中创建对象,使用new 关键字。
const myCar = new Car('red')
并且你可以使用extend 关键字来扩展一个现有的类。
class ElectricCar extends Car {
//...
}
你可以在构造函数和方法中调用super() ,以调用扩展类的相应方法。
访问器
字段可以有获取器和设置器。例子。
class Car {
private _color: string
get color(): string {
return this._color
}
set color(color: string) {
this._color = color
}
}
抽象类
类可以被定义为抽象的,这意味着需要有一个类来扩展它,并实现其最终的抽象方法。
abstract class Car {
abstract drive()
}
class SportsCar extends Car {
drive() {
console.log('You are driving a sports car')
}
}
接口
接口建立在基本类型之上。你可以把一个接口作为一个类型,这个接口可以包含其他类型的定义。
interface SetOfNumbers {
a: number;
b: number;
}
const multiply = (set: SetOfNumbers) => {
return set.a * set.b
}
multiply({ a:1, b: 2 })
一个接口也可以是一个类实现的接口。
interface Car {
name: 'string'
new (brand: string)
drive(): void
}
class SportsCar implements Car {
public name
construtor(public brand: string) {
//...
}
drive() {
console.log('You are driving a sports car')
}
}
函数的特点
函数可以在参数名称后面使用? 符号来拥有可选参数。
class Car {
drive(kilometers?: number) {
if (kilometers) {
console.log(`Drive the car for ${kilometers} kilometers`)
} else {
console.log(`Drive the car`)
}
}
}
和参数也可以有默认值。
class Car {
drive(kilometers = 10) {
console.log(`Drive the car for ${kilometers} kilometers`)
}
}
一个函数可以通过使用休息参数来接受不同数量的参数。
class Car {
drive(kilometers = 10, ...occupants: string[]) {
console.log(`Drive the car for ${kilometers} kilometers, with those people on it:`)
occupants.map((person) => console.log(person))
}
}
(new Car()).drive(20, 'Flavio', 'Roger', 'Syd')
枚举
枚举是定义命名常量的一种很好的方式,不幸的是JavaScript不支持这种方式,但被其他语言所普及。
TypeScript给了我们枚举。
enum Order {
First,
Second,
Third,
Fourth
}
TS内部为每个值分配了一个唯一的标识符,我们可以引用Order.First ,Order.Second ,等等。
你可以明确地给常量赋值。
enum Order {
First = 0,
Second = 1,
Third = 2,
Fourth = 3
}
或者也可以使用字符串。
enum Order {
First = 'FIRST',
Second = 'SECOND',
Third = 'THIRD',
Fourth = 'FOURTH'
}
泛型
泛型是许多不同的编程语言的一个特点。简而言之,你可以创建一个适用于不同类型的函数、接口或类,而无需事先指定类型。
但在编译时,如果你开始使用该函数的类型,然后你改变了类型(例如从数字到字符串),编译器会抛出一个错误。
我们可以通过完全省略类型或使用any ,但有了泛型,所有的工具都可以帮助我们。
语法示例。
function greet<T>(a : T) {
console.log(`Hi ${a}!`)
}
greet('Flavio')
有趣的T 符号标识了一个通用类型。
使用extends 关键字,该类型可以被限制在某个类家族或接口上。
interface Greetable { name: string }
function greet<T extends Greetable>(a : T) {
alert(`Hi ${a.name}!`)
}
greet({ name: 'Flavio'})
我已经准备好接受更多的东西了!
这些是TypeScript的基础知识。请到官方文档中学习所有的细节,或者开始编写你的应用程序,边写边学吧!