gpt4 book ai didi

angular - 为什么 TypeScript 接受值作为数据类型?

转载 作者:行者123 更新时间:2023-12-02 06:45:56 25 4
gpt4 key购买 nike

为什么 TypeScript 接受值作为数据类型?

下面的这些场景是接受和 Not Acceptable 声明。

export class MyComponent{
error: 'test' = 'test'; // accept
error: 'test' = 'test1'; // not accept
error: Boolean = true || false; // accept
error: true | false = true; // not accept
error: true = true; // accept
error: true = false; // not accept
error: Boolean; //accept
error: true; // accept
error: 1 = 1; //accept
error: 1 = 2; // not accept
}

  • Why does TypeScript allow a value as a data type?
  • How does JavaScript handle these at compile time?
  • How does it differ from readonly and constant?

readonly error= 'test';error: 'test' = 'test';

最佳答案

首先,一些非正式的背景和信息将有助于我们讨论您提出的问题:

通常,类型表示一组 0 个或多个值。可以将这些值视为该类型的成员或居民。

就它们可以采用的这种多重值而言,类型往往属于 3 组中的 1 组。

第 1 组: 例如:string 类型。 string 类型包含所有字符串值。由于字符串实际上可以是任意长的,因此本质上有无数个属于 string 类型的值。作为此类型成员的值集是所有可能字符串的集合。

第 2 组: 例如:undefined 类型。 undefined 类型只有一个值,即 undefined 值。因此,这种类型通常被称为单例类型,因为它的成员集只有 1 个值。

第 3 组: 例如:never 类型。 never 类型没有成员。根据定义,不可能有类型为 never 的值。当您在专业人士中阅读它时,这似乎有点令人困惑,但一个小代码示例可以解释它。

考虑:

function getValue(): never {
throw Error();
}

在上面的例子中,函数 getValue 的返回类型为 never,因为它从不返回值,它总是抛出。因此如果我们写
const value = getValue();
value 也是 never 类型。

现在开始第一个问题:

Why typescript allow value as a data type?



有很多很多原因,但有几个特别引人注目的原因是

对根据传递给它们的值而表现不同的函数的行为进行建模。想到的一个例子是函数 document.getElementsByTagName 。此函数始终采用 string 类型的值,并始终返回包含 NodeListHTMLElement 。但是,根据传递的实际字符串值,它将返回该列表中完全不同类型的内容。这些元素之间唯一的共同点是它们都来自 HTMLElement

现在,让我们考虑如何写下这个函数的类型签名。我们的第一刺可能是这样的
declare function getElementsByTagName(tagname: string): NodeList<HTMLElement>;

这是正确的,但不是特别有用。想象一下,我们想要获取页面上所有 HTMLInput 元素的值,以便我们可以将它们发送到我们的服务器。

我们知道, getElementsByTagName('input') 实际上只返回页面上的输入元素,正是我们想要的,但是根据我们上面的定义,虽然我们当然得到了正确的值(TypeScript 不影响 JavaScript 运行时行为),但它们会产生错误类型。具体来说,它们将是 HTMLElement 类型,即 HTMLInputElement 的父类(super class)型,它没有我们想要访问的 value 属性。

所以,我们能做些什么?我们可以将所有返回的元素“强制转换”为 HTMLInputElement 但这很丑陋,容易出错(我们必须记住所有类型名称以及它们如何映射到它们的标签名称),冗长,有点迟钝,我们知道得更好,并且我们静态地知道得更好。

因此,需要对 tagname 的值(即 getElementsByTagName 的参数)与其实际返回的元素类型之间的关系进行建模。

输入字符串文字类型:

字符串文字类型是一种更精炼的字符串类型,它是一种单例类型,就像 undefined 一样,它只有一个值,即文字字符串。一旦我们有了这种类型,我们就可以重载 getElementsByTagName 的声明,使其更加精确和有用
declare function getElementsByTagName(tagname: 'input'): NodeList<HTMLInputElement>;
declare function getElementsByTagName(tagname: string): NodeList<HTMLElement>;

我认为这清楚地展示了具有从值派生并仅由该单个值居住的专用字符串类型的效用,但还有很多其他原因需要使用它们,所以我将再讨论一些。

在前面的示例中,我会说易用性是主要动机,但请记住,TypeScript 的第一目标是通过编译时、静态分析来捕获编程错误。

鉴于此,另一个动机是精确。有很多很多 JavaScript API 都采用特定的值,并且取决于它是什么,它们可能会做一些非常不同的事情,什么都不做,或者很难失败。

因此,对于另一个真实世界的例子,SystemJS 是一个优秀且广泛使用的模块加载器,它具有广泛的配置 API。您可以传递给它的选项之一称为 transpiler,根据您指定的值,会发生非常不同的事情,此外,如果您指定无效值,它将尝试加载一个不存在的模块,并且无法加载其他任何东西。 transpiler 的有效值为: "plugin-traceur""plugin-babel""plugin-typescript" 和 0x25181214131331。我们不仅要让 TypeScript 建议这 4 种可能性,还要让它检查我们是否只使用了这些可能性中的一种。

在我们可以使用离散值作为类型之前,这个 API 很难建模。

充其量,我们将不得不写一些类似的东西
transpiler: string | boolean;

这不是我们想要的,因为只有 3 个有效字符串并且 false 不是有效值!

通过使用值作为类型,我们实际上可以完美精确地将这个 API 描述为
transpiler: 'plugin-traceur' | 'plugin-babel' | 'plugin-typescript' | false;

并且不仅知道我们可以传递哪些值,而且如果我们错误地输入 true 或尝试传递 'plugin-tsc' ,则会立即得到错误。

因此,文字类型可以及早捕获错误,同时实现对 Wilds 中现有 API 的精确描述。

另一个好处是控制流分析,它允许编译器检测常见的逻辑错误。这是一个复杂的话题,但这里有一个简单的例子:
declare const compass: {
direction: 'N' | 'E' | 'S' | 'W'
};

const direction = compass.direction;
// direction is 'N' | 'E' | 'S' | 'W'
if (direction === 'N') {
console.log('north');
} // direction is 'E' | 'S' | 'W'
else if (direction === 'S') {
console.log('south');
} // direction is 'E' | 'W'
else if (direction === 'N') { // ERROR!
console.log('Northerly');
}

上面的代码包含了一个比较简单的逻辑错误,但是条件复杂,人为因素多,在实践中非常容易遗漏。第三个 true 本质上是死代码,它的主体永远不会被执行。文字类型赋予我们将可能的罗盘 if 声明为“N”、“S”、“E”或“W”之一的特殊性使编译器能够立即将第三个 if 语句标记为无法访问的、有效无意义的代码,表明我们程序中的一个错误,一个逻辑错误(毕竟我们只是人类)。

因此,我们有一个主要的插入因素,能够定义与可能值的非常特定的子集相对应的类型。最后一个例子最好的部分是它都在我们自己的代码中。我们想声明一个合理但高度具体的契约(Contract),语言为我们提供了表达能力,然后在我们违反自己的契约(Contract)时捕获了我们。

And how Javascript handle those on compile time?



与所有其他 TypeScript 类型完全相同的方式。它们从 TypeScript 编译器发出的 JavaScript 中完全删除。

And How it will differ from readonly and constant?



与所有 TypeScript 类型一样,您所说的类型,即表示特定值的类型,与 directionconst 修饰符交互。这种交互有点复杂,可以浅显地解决,就像我在这里做的那样,或者很容易就包含一个 Q/A 本身。

可以这么说, readonlyconst 对可能的值有影响,因此变量或属性实际上可以在任何时候保存的可能类型,因此使文字类型,特定值的类型,更容易传播,推理关于,也许最重要的是为我们推断。

因此,当某些东西是不可变的时,通常尽可能地推断它的类型是有意义的,因为它的值不会改变。
const x = 'a'; 

推断 readonly 的类型为 x,因为它无法重新分配。
let x = 'a';

另一方面,推断 'a' 的类型为 x,因为它是可变的。

现在你可以写
let x: 'a' = 'a';

在这种情况下,虽然它是可变的,但它只能被分配一个 string 类型的值。

请注意,为了说明目的,这有点过于简单化了。

在上面的 'a' 示例中可以观察到,还有其他机制在起作用,该示例表明该语言还有另一层,即控制流分析层,它跟踪可能的值类型,因为它们被条件、赋值、真实检查缩小了范围,以及其他结构,如解构赋值。

现在让我们逐个属性地详细检查您问题中的类:
export class MyComponent {
// OK because we have said `error` is of type 'test',
// the singleton string type whose values must be members of the set {'test'}
error: 'test' = 'test';
// NOT OK because we have said `error` is of type 'test',
// the singleton string type whose values must be members of the set {'test'}
// 'test1' is not a member of the set {'test'}
error: 'test' = 'test1';
// OK but a word of Warning:
// this is valid because of a subtle aspect of structural subtyping,
// another topic but it is an error in your program as the type `Boolean` with a
// capital "B" is the wrong type to use
// you definitely want to use 'boolean' with a lowercase "b" instead.
error: Boolean = true || false;
// This one is OK, it must be a typo in your question because we have said that
// `error` is of type true | false the type whose values must
// be members of the set {true, false} and true satisfies that and so is accepted
error: true | false = true;
// OK for the same reason as the first property, error: 'test' = 'test';
error: true = true;
// NOT OK because we have said that error is of type `true` the type whose values
// must be members of the set {true}
// false is not in that set and therefore this is an error.
error: true = false;
// OK this is just a type declaration, no value is provided, but
// as noted above, this is the WRONG type to use.
// please use boolean with a lowercase "b".
error: Boolean;
// As above, this is just a type, no value to conflict with
error: true;
// OK because we have said `error` is of type 1 (yes the number 1),
// the singleton number type whose values must be members of the set {1}
// 1 is a member of {1} so we are good to go
error: 1 = 1;
// NOT OK because we have said `error` is of type 1 (yes the number 1),
// the singleton number type whose values must be members of the set {1}
// 2 is NOT a member of {1} so this is an error.
error: 1 = 2;
}

TypeScript 是关于类型推断的,推断越多越好,因为它能够传播来自值的类型信息,例如表达式,并使用它来推断更精确的类型。

在大多数语言中,类型系统从类型开始,但在 TypeScript 中,情况几乎一直如此,类型系统从值开始。所有值都有类型。对这些值的操作产生具有新类型的新值,允许类型干扰进一步传播到程序中。

如果你将一个普通的 JavaScript 程序粘贴到一个 TypeScript 文件中,你会注意到,在不添加任何类型注释的情况下,它能够弄清楚你的程序的结构。文字类型进一步增强了这种能力。

关于文字类型还有很多可以说的,为了解释的目的,我已经省略和简化了某些东西,但请放心,它们很棒。

关于angular - 为什么 TypeScript 接受值作为数据类型?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44560995/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com