Object-oriented programming (OOP) has been one of the most influential paradigms in the development of modern programming practices. If you've ever thought in terms of classes or inheritance, you prove the point: Those ideas have their origins in classical OOP.
面向对象编程(OOP)一直是现代编程实践发展中最有影响力的范例之一。 如果您曾经考虑过类或继承方面的问题,那么您可以证明这一点:这些想法起源于经典的OOP。
TypeScript is a language that adds object-oriented syntactical sugar to JavaScript
TypeScript是一种向JavaScript添加面向对象的语法糖的语言
Object-oriented programming has been so influential that many can't imagine doing things any other way. TypeScript's very existence is testament to this fact: It provides classically object-oriented syntax for a language that supports almost no classical object-oriented features.
面向对象的编程非常有影响力,以至于许多人无法想象以其他方式来做事情。 TypeScript的存在就证明了这一事实:它为几乎不支持经典面向对象功能的语言提供了经典的面向对象语法。
Make no mistake: TypeScript does not add true classes, interfaces, or generics. Just some sugar to make it easier if you think that way. Regardless, it's a good introduction to the concepts of OOP for people whose background is mostly in JavaScript.
没错:TypeScript不会添加真实的类,接口或泛型。 如果您那样想的话,只需加些糖即可使其变得更容易。 无论如何,对于背景知识主要是JavaScript的人们来说,它都是对OOP概念的很好的介绍。
This series has two goals:
本系列有两个目标:
Those in the first group should find it easy to map the concepts we discuss to the syntax we'll see. Those in the latter group should find it easy to focus on the concepts without getting distracted by substantially different syntax.
第一组中的人应该发现将我们讨论的概念映射到我们将看到的语法很容易。 后一组的人应该容易集中精力于这些概念,而不会因语法的实质差异而分心。
We'll ease into it, and start in familiar territory. After reading this article, you'll be able to:
我们将简化它,并从熟悉的领域入手。 阅读本文之后,您将能够:
To get started, you'll need to install TypeScript (TS). In this article, we'll be using Node.js, v5.11.0, and NPM, v3.8.6. You can also get TS bundled with Visual Studio 2015, if you prefer.
首先,您需要安装TypeScript(TS)。 在本文中,我们将使用Node.js v5.11.0和NPM v3.8.6。 如果愿意,您还可以将TS与Visual Studio 2015捆绑在一起。
To install TypeScript, run:
要安装TypeScript,请运行:
npm install --global typescript
This installs a command-line tool, tsc
, which you use to transpile TS to JavaScript.
这将安装一个命令行工具tsc
,您可以使用该工具将TS转换为JavaScript。
Create a new directory somewhere: We'll save and compile our TS examples here.
在某处创建一个新目录:我们将在此处保存并编译我们的TS示例。
# This creates a new directory in your home folder.
# Feel free to do this somewhere else.
cd && mkdir ts_examples
While simple, I've collected all of the examples in a GitHub repo. If you don't want to type, run:
尽管简单,但我已将所有示例收集在GitHub存储库中 。 如果您不想输入,请运行:
git clone https://github.com/Peleke/oop_in_typescript
git checkout Part_1-Types
. . . And you're good to go.
。 。 。 而且你很好。
The word type means different things in different contexts. In general, types are a way of classifying data structures based on the kind of information they can contain, and what you can do with it.
类型一词在不同的上下文中意味着不同的事物。 通常, 类型是一种基于数据结构可以包含的信息种类以及对数据结构进行分类的方法。
In JavaScript, clause 6 of the ECMAScript specification defines the following types:
在JavaScript中, ECMAScript规范的第6条定义了以下类型:
To say a variable is of a certain type is to say two things:
说一个变量是某种类型,就是说两件事:
At the level of applications programming, we're only concerned with the first point.
在应用程序编程级别,我们只关心第一点。
"use strict";
const name = "Peleke"; // name has type string
const nothing = null; // nothing has type null -- kind of*
const nemo = undefined; // nemo has type undefined
const obj = { }; // obj has type object
// * Brendan Eich made a mistake in the 10-day whirlwind of putting together JavaScript.
// typeof null returns "object", but it's not an object, and this is a bug.
// Under the hood, null /is/ its own primitive type -- Null. You just can't tell.
// For more: http://www.2ality.com/2013/10/typeof-null.html
Of course, if you've spent more than a few minutes writing JavaScript, you'll know that this is mostly trivia. We don't annotate types in JavaScript, and we can assign values of different types to the same variables through the lifetime of our program.
当然,如果您花了几分钟的时间编写JavaScript,您就会知道这主要是琐事。 我们不在JavaScript中注释类型,并且可以在程序的生命周期内将不同类型的值分配给相同的变量。
As far as we're concerned, if an object has the properties and methods we need it to, we don't care what the underlying type is. If it behaves properly, it's good enough. The rest is an implementation detail.
就我们而言,如果对象具有我们需要的属性和方法,则我们不在乎底层类型是什么。 如果行为正常,那就足够了。 其余的是实现细节。
This is sometimes called duck-typing: If it walks like a duck, and it quacks like a duck, it's probably okay if we eat it treat it like a duck.
有时这被称为鸭式打字 :如果它像鸭子一样行走,而像鸭子一样嘎嘎叫,如果我们把它像鸭子一样对待就可以了。
Languages like Java and Haskell are statically typed. That means that, before they run your program, they'll check to make sure that you get strings where you expect strings, numbers where you expect numbers, and mongeese where you expect mongeese. If you pass a mongoose to a function that expects a string, it'll complain, and you'll have to fix it before your program will compile.
诸如Java和Haskell之类的语言是静态类型的 。 这意味着,在他们运行程序之前,他们将检查以确保您在期望字符串的位置得到了字符串,在期望数字的位置得到了数字,并在期望英语的地方得到了mongeese。 如果将猫鼬传递给需要字符串的函数,它将发出抱怨,并且必须在程序编译之前对其进行修复。
The point of all that trouble is to let you know when you've written code that might break before you run it. After all, if you write a function that expects a string, and you give it a mongoose, chances are good it's not going to know what to do with it.
所有麻烦的点在于,在编写可能会在运行前中断的代码时让您知道。 毕竟,如果您编写了一个期望字符串的函数,并且给了它一个猫鼬,那么很有可能它不会知道如何处理它。
private boolean screamName(String name) {
return name.toUpperCase();
}
// Mongeese don't like to be capitalized.
// Fortunately, the compiler prevents this from building.
tameMongoose(new Mongoose());
In JavaScript, we'd just go ahead and pass the mongoose along and cross our fingers. If it's got a toUpperCase
, it works. If not, our program dies. Devil's gambit.
在JavaScript中,我们将继续前进,使猫鼬穿过并越过我们的手指。 如果它具有toUpperCase
,那么它将起作用。 如果没有,我们的程序就会死掉。 魔鬼的陷阱。
"use strict";
const sweet_mongoose = { };
const savage_mongoose = {};
sweet_mongoose.name = "sweetheart";
savage_mongoose.name = "savage";
sweet_mongoose.toUpperCase = function () { return this.name.toUpperCase(); }
function screamName (name) {
return name.toUpperCase();
}
screamName(sweet_mongoose); // What a SWEETHEART.
screamName(savage_mongoose); // Kills program with a TypeError. Cold-blooded.
The idea behind all this is that a value's type can be conceived of as a sort of interface. In other words, a object's type reveals what you can and cannot do with it. Attempting to do something with an object that doesn't make sense for its type -- like capitalizing a number -- is a programmer error that a type-aware compiler can catch ahead of time.
所有这些背后的想法是,可以将值的类型视为一种接口 。 换句话说,对象的类型揭示了您可以做什么和不能做什么。 尝试对类型没有意义的对象执行某些操作(例如将数字大写)是程序员的错误,类型识别编译器可能会提前捕获该错误。
Therein lies one of the benefits: We trade runtime errors for compile-time warnings. By contrast, dynamic languages eschew compile-time safety checks in favor of greater flexibility and programmer productivity.
其中的好处之一是:我们将运行时错误替换为编译时警告。 相比之下,动态语言避免了编译时安全检查,而是希望获得更大的灵活性和程序员生产率。
Obviously, neither system is perfect:
显然,这两个系统都不是完美的:
toUpperCase
method that behaved as desired? Then we'd have to do some finaggling just to get our program to compile. 在我们的Java函数中,如果猫鼬确实具有表现理想的toUpperCase
方法,该怎么办? 然后,我们必须做些费劲的事情才能使我们的程序得以编译。 subscribe
methods. We'll obviously notice an error, but there's a substantial chance the program would execute -- just not correctly. 在Javascript中,如果我们获得具有期望功能但行为不同的对象该怎么办? Observable和RSS feed具有非常不同的subscribe
方法。 我们显然会注意到一个错误,但是程序很有可能会执行 -只是执行不正确 。 That said:
说:
Do we need static typing? Obviously not. JavaScript doesn't have it, and the Internet works fine.
我们需要静态输入吗? 显然不是。 JavaScript没有它,并且Internet可以正常工作。
Do we benefit from it? Maybe. It's powerful in languages like Java. It's exceedingly powerful in certain statically-typed functional languages, like ML, Haskell, and Scala.
我们从中受益吗? 也许。 它在Java之类的语言中功能强大。 在某些静态类型的功能语言(例如ML,Haskell和Scala)中,它的功能极其强大。
TypeScript takes the stance that we unequivocally do benefit from static typing, allowing you to declare your variables with any of a handful of built-in types.
TypeScript采取的立场是,我们无疑会从静态类型中受益,这使您可以使用几种内置类型中的任何一种来声明变量。
TypeScript exposes nine types for us to work with. Whether you use them or not is up to you: Type annotations are always optional in TypeScript.
TypeScript提供了九种类型供我们使用。 是否使用它们取决于您: 类型注释在TypeScript中始终是可选的 。
We'll start with the first eight, and circle back to the last one a bit later.
我们将从前八个开始,然后再转回到最后一个。
In TypeScript, you declare variables as in JavaScript, with a colon and type after the variable name. Booleans behave as usual.
在TypeScript中,您像在JavaScript中一样声明变量,并用冒号表示,并在变量名后键入。 布尔值的行为与往常一样。
Type Keyword : boolean
类型关键字 : boolean
"use strict";
const lie : boolean = false,
truth : boolean = true;
Save that in a file called boolean.ts
, then run tsc boolean.ts
. It'll create a file called boolean.js
, Open it up to see some sweet, sweet vanilla JavaScript:
将其保存在名为boolean.ts
的文件中,然后运行tsc boolean.ts
。 它将创建一个名为boolean.js
的文件,将其打开以查看一些甜美的原始JavaScript:
"use strict";
var lie = false, truth = true;
TypeScript's Number type is synonymous with JavaScript's Number type. If youre curious, JavaScript numbers are an IEEE 64-bit binary format double precision floating point values. Or, you know, just numbers.
TypeScript的Number类型与JavaScript的Number类型同义。 如果您感到好奇,JavaScript数字是IEEE 64位二进制格式的双精度浮点值 。 或者,您知道,只是数字。
Type Keyword : number
类型关键字 : number
"use strict";
const pi : number = 3.14159;
As with Number, TypeScript's String is synonymous with JavaScript's underlying String type.
与Number一样,TypeScript的String与JavaScript的基础String类型同义。
Type Keyword : string
类型关键字 : string
"use strict";
const tree_of_knowledge : string = "Yggdrasil";
TypeScript treats arrays as their own type, and requires that you declare the type of what's inside of them, as well.
TypeScript将数组视为自己的类型,并且还要求您声明数组内部的类型。
** Type Keywords **
**类型关键字**
There are two syntaxes for arrays.
数组有两种语法。
"use strict";
// The item type, T, followed by brackets means, "an array whose items are of type T."
const divine_lovers : string[] = ["Zeus", "Aphrodite"];
// Writing Array<[Item Type]> means the same thing.
const digits : Array<number> = [143219876, 112347890];
// But this doesn't work. Hm. . .
const only_strings : string[] = [];
only_strings.push("This Works!")
only_strings.push(42); // This doesn't.
Well. That's annoying.
好。 真烦人
We can't put 42
into only_strings
because we declared that it can only contain items of type string
. This is an example of TypeScript's compile-time complaints in action.
我们不能将42
放入only_strings
因为我们声明它只能包含string
类型的项目。 这是TypeScript编译时投诉的一个示例。
We'll see how to fix this shortly. But first -- tuples!
我们将在短期内解决此问题。 但是首先-元组!
Tuples are another collection data type. They're useful for collecting a known number of items into an array-like structure. You can declare each item to be of a specific type.
元组是另一种收集数据类型。 它们对于将已知数量的项目收集到类似数组的结构中很有用。 您可以将每个项目声明为特定类型。
** Type Keyword ** : [(Item Type), . . . , (Item Type)]
**类型关键字**:[(项目类型),。 。 。 , (物品种类)]
"use strict";
// [Date, Month, Year] :: Triplet of numbers
let date_triplet : [number, number, number];
date_triplet = [31, 6, 2016];
// [Name, Age]
let athena : [string, number];
athena = ['Athena', 9386];
// You retrieve items with indexes, as with arrays.
// TS remembers the type for you.
var name : string = athena[0];
const age : number = athena[1];
// But:
name = athena[1]; // No dice. We're writing TypeScript now.
So . . . What are tuples useful for?
这样。 。 。 元组有什么用?
Good question. JavaScript is the only language I write without native tuples, but I rarely use them elsewhere. Occasionally, I use them to group related data that isn't liable to change:
好问题。 JavaScript是我编写的唯一没有本机元组的语言,但是我很少在其他地方使用它们。 有时,我使用它们来对不易于更改的相关数据进行分组:
"use strict";
//The Big Three of Powerlifting
let big_three : [string, string, string];
big_three = ['Squat', 'Deadlift', 'Bench Press']
. . . But in general, I use a Map or Set instead.
。 。 。 但总的来说,我改用Map或Set。
Enums allow you to associate names with intever values. The name comes from the fact that they're *enumerable*.
枚举允许您将名称与多个值关联。 这个名字源于它们“ 枚举”的事实。
Creating a enum effectively creates a new type keyword, which you use like any other type.
创建枚举有效地创建了一个新类型的关键字,您可以像使用其他任何类型一样使用它。
"use strict";
// This is an example from the TypeScript docs.
// http://bit.ly/1XQjl2Y
enum Color { Red, Green, Blue };
const red : Color = Color.Red;
// Enums are like associative arrays. Each enum constant is associated with an index, starting at 0.
console.log(Color[0]); // 'Red'
// You can start from any number instead of zero, if you want.
// Note that indexing a non-existent enum constant returns undefined,
// but doesn't throw an error.
enum RomanceLanguages { Spanish = 1, French, Italian, Romanian, Portuguese };
console.log(RomanceLanguages[4]); // 'Romanian'
console.log(RomanceLanguages[0]); // undefined
Symbols provide an interesting alternative to enums.
符号提供了一个有趣的替代枚举 。
Any is more or less what it sounds like: A type that accepts any value. In some sense, it's a way to opt out of type checking. It also allows us to declare mixed collections.
Any或多或少听起来像:接受任何值的类型。 从某种意义上说,这是一种选择退出类型检查的方法。 它还允许我们声明混合集合。
** Type Keyword ** : any
**类型关键字**: any
"use strict";
let mystery : any = 4; // number
mystery = "four"; // string -- no error
const not_only_strings : any[] = [];
not_only_strings.push("This Works!")
not_only_strings.push(42); // This does too.
Finally, we have Void. This is the typeclass associated with the values undefined
or null
. Unlike the other types, you won't use this to declare variables.
最后,我们有虚空。 这是与值undefined
或null
关联的类型类。 与其他类型不同,您不会使用它来声明变量。
It's not that you can't. Just that, if you do, you can only set that varible to null
or undefined
.
不是你不能 。 即便如此,您也只能将该变量设置为null
或undefined
。
"use strict";
let the_void : void = undefined;
the_void = null;
the_void = "nothing"; // Error.
The main reason void
exists is to mark functions without return statements.
存在void
主要原因是标记没有return语句的函数。
Speaking of which, it's about time we talk about functions.
说到这,是时候讨论功能了。
TypeScript allows you to mark the type of the value that a function returns.
TypeScript允许您标记函数返回的值的类型。
Just as a variable's type indicates what sort of data the variable contains, a function's return type indicates what sort of data it returns.
正如变量的类型指示变量包含的数据类型一样,函数的返回类型指示其返回的数据类型。
All the types we've seen so far are valid return types. In particuar, you'll use void
almost exclusively as the return type of a function with no return
statement.
到目前为止,我们看到的所有类型都是有效的返回类型。 特别是,您将几乎完全使用void
作为没有return
语句的函数的返回类型。
You denote a return type the same way you denote a variable type: With a colon and a type keyword, this time after the arguments list.
表示返回类型的方式与表示变量类型的方式相同:这次带有一个冒号和一个type关键字,这一次是在参数列表之后。
"use strict";
function capitalizeName (name : string) : string {
return name.toUpperCase();
}
// Shoutout to @achm4m0 (Alberto) for notifying me of an error here!
console.log(capitalizeName('geronimo')); // 'GERONIMO'
// But:
console.log(capitalizeName(42)); // Error; 42 isn't a string.
// TypeScript checks at compile-time if you're returning the right type.
function even_broken (num : number) : boolean {
return (num % 2); // WRONG. This will cause a compile-time error.
}
function even (num : number) : boolean {
return (num % 2 == 0); // Much better; this works.
}
Note that you can also mark argument types as you would that of normal variables.
请注意,您也可以像普通变量一样标记参数类型。
Notifications like the one we get for even_broken
are pretty helpful during development.
像我们为even_broken
收到的通知在开发过程中非常有用。
One of JavaScript's greatest strengths is that it treats functions as first class citizens. This means we can pass them as arguments and return them from functions ike any other value. How does TypeScript handle that?
JavaScript的最大优点之一就是将函数视为一流的公民。 这意味着我们可以将它们作为参数传递,并从其他任何值的函数中返回。 TypeScript如何处理呢?
Simple. Function name; parenthetical list of typed arguments; fat arrow; return type.
简单。 函数名称; 类型参数的括号列表; 胖箭 返回类型。
"use strict";
let multiply : (first : number, second : number) => number;
multiply = function (first, second) {
return first * second;
}
This example illustrates that you can annotate your functions types before defining them.
此示例说明了可以在定义函数类型之前对其进行注释。
The same pattern applies if you're returning a function. In that case, though, you wrap the type of the function you return -- that is, everything after the fat arrow -- in parentheses.
如果要返回函数,则适用相同的模式。 不过,在这种情况下,您可以将返回的函数的类型(即,粗箭头后的所有内容)括在括号中。
"use strict";
let multiplyFirst : ( first : number) => ((second : number) => number);
multiplyFirst = function (first) {
return function (num) {
return first * num;
}
}
console.log(multiply(5, 2)); // '10'
console.log(multiplyFirst(9)(2)); // '18'
Newcomers to static typing often find variable types annoying. Function types are where they start to get nauseated.
静态类型的新手经常会发现烦人的变量类型。 函数类型是它们开始令人讨厌的地方。
This is actually the typing feature I use most in TypeScript. Somewhat ironically, that's because of my background in functional programming, not OOP, and that remains the context in which I find it most useful.
实际上,这是我在TypeScript中使用最多的键入功能。 具有讽刺意味的是,这是因为我在函数式编程方面而不是在OOP方面具有背景知识,而这仍然是我认为最有用的上下文。
Annotating functions that return string
or number
is easy, and beneficial largely for the purpose of self-documentation.
返回string
或number
注释函数很容易,并且在很大程度上有利于自我记录。
On the other hand, writing function type annotations is an integral, active part of my design process when I work with Rx streams and complex [function compositions](https://en.wikipedia.org/wiki/Function_composition_(computer_science).
另一方面,当我使用Rx流和复杂的[function composition]( https://en.wikipedia.org/wiki/Function_composition_ ( computer_science)时,编写函数类型注释是设计过程中不可或缺的活动部分。
By now, you should feel like you've started getting your hands dirty with TypeScript. You've seen:
到现在为止,您应该感觉已经开始使用TypeScript弄脏了手。 你看过:
You should also feel pretty confident that you can article why we might benefit from all this in the first place.
您还应该对自己的文章充满信心, 因为我们可以首先说明为什么我们可以从所有这些中受益。
Next time, we'll stray a little farther from familiar territory, and explore classes; interfaces; enums; and how to get the most out of them by adapting classical obect-oriented design to our more dynamic environment.
下次,我们将离熟悉的地区更远,探索课程; 接口; 枚举 以及如何通过使面向对象的经典设计适应我们更加动态的环境来充分利用它们。
Until then, feel free to drop questions in the comments below, or shoot them to me on Twitter (@PelekeS).
在此之前,请随时在下面的评论中提问,或在Twitter( @PelekeS )上向我射击。
翻译自: https://scotch.io/tutorials/from-javascript-to-typescript-pt-i-types-variables