我可以在块范围内输入变量吗?

时间:2014-08-21 07:50:04

标签: typescript

我有这段代码:

function foo(input: Blob): void;
function foo(input: string): void;
function foo(input: any) {
    if (typeof input === "string") {

    }   
    else if (input instanceof Blob) {

    }
}

在这里,我想在块范围内输入input,而不是将其重新分配给另一个变量。

if (typeof input === "string") {
    declare input: string;
}   
else if (input instanceof Blob) {
    declare input: Blob;
}

而不是:

if (typeof input === "string") {
    var str: string = input;
}   
else if (input instanceof Blob) {
    var blob: Blob = input;
}

这在TypeScript中是否可行?

3 个答案:

答案 0 :(得分:1)

不可能。您需要具有不同类型的新变量名称。

答案 1 :(得分:1)

我认为最新版本的TypeScript会自动执行此操作。但您必须与typeofinstanceof或特殊函数一起使用if语句。

function foo(input: string|blob|whatever) {
    if(typeof input === 'string') {
        // input is now a string
        input.blobfunction(); // Error: blobfunction() is not a method of string
    } else if(input instanceof blob) {
        // input is now a blob
    } else if(isWhatever(input)) {
        // input is now a whatever
    }
}

// Probably a good idea to make this a static 'whatever.isInstance()'
function isWhatever(input: string|blob|whatever): input is whatever {
    return true; // put your test for whatever here
}

请参阅:https://www.typescriptlang.org/docs/handbook/advanced-types.html

答案 2 :(得分:0)

开始!新版本有增强的推理能力!

类似的东西

const a: string | string[];

if (Array.isArray(a)) {
  a.map() // array here (automatically inferred)
} else {
  a.trim() // string here (automatically inferred)
}

enter image description here

enter image description here

编译器可以毫无问题地推断出很多地方!


// typeof

if (typeof a === 'number') {
   // number
}

// instance of

if (a instanceof SomeClass) {
   // SomeClass
}

编译器在很多情况下都很聪明地进行推断!哪里很清楚!

铸造和整个范围

问题!

丑陋的代码如:

if (PREDEFINED_STRATEGIES[(settings.strategy as keyof typeof PREDEFINED_STRATEGIES)]) {
  StrategyClass = PREDEFINED_STRATEGIES[
    settings.strategy as keyof typeof PREDEFINED_STRATEGIES
  ];
} else {

每次都添加 as keyof typeof PREDEFINED_STRATEGIES 进行转换并不酷!

没有在整个范围内投射一次的原生机制!

原生方式在你施放它的地方使用另一个变量

const strategy = settings.strategy as keyof typeof PREDEFINED_STRATEGIES;

if (PREDEFINED_STRATEGIES[strategy]) {
  StrategyClass = PREDEFINED_STRATEGIES[strategy];
} else {

然而,这将创建一个不必要的变量!这将在运行时运行!

这是一个代码示例:

ts

interface IStrategy {
  init(settings: any): Promise<this>,
  getAmount(): number
}

export type StrategyClass = new () => IStrategy;

interface IPredefinedStrategies {
  [name: string]: StrategyClass
}
const PREDEFINED: IPredefinedStrategies = {
  'SomeStrategy': class SomeStrategy {} as StrategyClass
};

const strategy: string | StrategyClass = 'someStrategy';

// <<<<<<<<<<<<<<< casting with a variable >>>>>>>>>>>>>>>
const mStrategy = strategy as keyof typeof PREDEFINED;

let strategyClass: StrategyClass;

if (PREDEFINED[mStrategy]) {
  strategyClass = PREDEFINED[mStrategy];
} else {
  strategyClass = class SomeStrategy { } as StrategyClass;
}

const instance = new strategyClass();

js 编译输出:

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const PREDEFINED = {
    'SomeStrategy': class SomeStrategy {
    }
};
const strategy = 'someStrategy';
const mStrategy = strategy; // <<<<<< you can see the variable here ending in final code >>>>>>
let strategyClass;
if (PREDEFINED[mStrategy]) {
    strategyClass = PREDEFINED[mStrategy];
}
else {
    strategyClass = class SomeStrategy {
    };
}
const instance = new strategyClass();
//# sourceMappingURL=index.tscVersion.js.map

您可能会问的问题? Typescript 是否优化了最终代码! (任何配置选项...)=> 核心上没有!

您可以查看here

<块引用>

非目标

如果你想要这样的东西!

可以想到三件事! 变形金刚!或者 js 优化器

JS 优化器

对于 js 优化器,我可以提到 Google Closure 编译器!在高级水平高度优化代码! 保持最小化处理和删除死代码!类似于 C++ 编译器优化!

例如不需要的(额外变量)将被自动删除!

可以让 ts 构建先行!然后在build文件夹上执行Closure编译器! (npm run build 脚本可以这样)

如果为浏览器构建东西! 闭包编译器的使用可以产生很大的不同!无论是大小还是优化!就像缩小的用法一样!在浏览器中 js 包大小!并且还解析编译所有事情!代码越小!更好!加上删除任何不必要的东西和 js 代码!你可以看看这个很棒的reference and document

简而言之,如果您已经在使用 Closure compiler使用局部变量进行转义只是最简单的好方法!我们可以依靠 Closure 编译器为我们移除它!

您可以检查Closure compiler here

知道有cli工具(jar文件(java中构建))!

Download page

我已经在我的计算机中设置了它(命令别名为 closureCompilercc

enter image description here

enter image description here

在Linux中:

.bashrc(.zshrc,如果您使用的是 .zshrc)

# Closure compiler

export CLOSURE_COMPILER_FILE=$HOME/opt/closureCompiler/closure-compiler.jar

closureCompiler() {
  java -jar $CLOSURE_COMPILER_FILE $@
}

cc() {
   closureCompiler $@
}

您可以在网络应用(playground)上查看该工具

DocTool (Playground)

您可以在下面的示例中查看优化是如何进行的:

Closure compiler optimization meaningless vars

我们可以看到无意义的变量是如何被删除的!

从我们的第一个例子中获取代码:

enter image description here

我们可以看到高级是如何优化的!

要知道 Closure Compiler 仍在不断更新!有很多选择!您可以通过打印 cli 命令帮助 (--help) 来检查!支持优化输出到不同的es目标! ...

ECMASCRIPT3, ECMASCRIPT5,
ECMASCRIPT5_STRICT, ECMASCRIPT_2015,
ECMASCRIPT_2016, ECMASCRIPT_2017,
ECMASCRIPT_2018, ECMASCRIPT_2019,
ECMASCRIPT_2020, STABLE,
ECMASCRIPT_NEXT (latest features)

对于Ts 内核不支持

https://github.com/microsoft/TypeScript/issues/4892

但是有一些工具可以添加如下内容:

https://github.com/blainehansen/macro-ts

(这个包是作者的实验!不是全部成熟!然后我很容易看到不必要的增加的复杂性!还有配置文件......!我没看太远!我不会判断包!)

>

我建议的是通过 ts 转换器功能。

想到宏!就像你写的那样!那么它就会被移除!

变形金刚和宏

让我先分享变压器资源! (与宏无关!他们是一个很好的话题)

https://blog.logrocket.com/using-typescript-transforms-to-enrich-runtime-code-3fd2863221ed/

https://github.com/madou/typescript-transformer-handbook

https://levelup.gitconnected.com/writing-typescript-custom-ast-transformer-part-1-7585d6916819

https://blogs.u2u.be/diedrik/post/typescript-transformers-transform-and-rise-up

<块引用>

不幸的是,现在 tsconfig.json 不允许指定自定义 AST 转换器。
您可以使用多种替代方法,每种方法都有自己的警告:

===

范围投射功能!本来可以通过直接变压器实现的! (没时间试这个)!

我准备了一个 repo(测试书)!详细说明方法!

我们将使用 ttypescript 包来使用带有转换器的打字稿!

<块引用>

Typescript 不通过 tsconfig 支持转换器!只能通过编码api!

使用 ttypescript!我们将能够通过 tsconfig.json 添加转换器。

我们将使用包装上的变压器typescript-transform-macros

这个转换器提供hygienic macro

在继续之前!如果你不知道它们是什么,你可以检查一些 ts 转换器的参考:

我尝试了所有这些!你可以在下面的 Repo 中查看它!有详细的解释!了解变压器的链接!代码示例!

https://github.com/MohamedLamineAllal/TypescriptMacroTestBook

宏示例:

declare function MACRO<T>(t: T): T;

interface IStrategy {
  init(settings: any): Promise<this>,
  getAmount(): number
}

export type StrategyClass = new () => IStrategy;

interface IPredefinedStrategies {
  [name: string]: StrategyClass
}
const PREDEFINED: IPredefinedStrategies = {
  'SomeStrategy': class SomeStrategy {} as StrategyClass
};

const strategy: string | StrategyClass = 'someStrategy';

// <<<<<<<<<<<<<<<< Defining macro >>>>>>>>>>>>>>>>
const mStrategy = MACRO(
  strategy as keyof typeof PREDEFINED
);

let strategyClass: StrategyClass;

if (PREDEFINED[mStrategy]) {
  strategyClass = PREDEFINED[mStrategy];
} else {
  strategyClass = class SomeStrategy { } as StrategyClass;
}

const instance = new strategyClass();

输出:

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const PREDEFINED = {
    'SomeStrategy': class SomeStrategy {
    }
};
const strategy = 'someStrategy'; // <<<<<< no macro >>>>>>
let strategyClass;
if (PREDEFINED[strategy]) {
    strategyClass = PREDEFINED[strategy];
}
else {
    strategyClass = class SomeStrategy {
    };
}
const instance = new strategyClass();
//# sourceMappingURL=index.js.map

你可以看到它是如何工作的!这是完美的基于作用域的投射方式!

对于宏转换器,您也可以查看 babel 宏插件链接:

https://github.com/codemix/babel-plugin-macros

宏、ttypsecript、转换器(矫枉过正?)

很酷的东西! ttypescript 很直观!感觉只是带有变形金刚支持的简单打字稿!一样的命令!只需添加一个额外的 t!所以没有矫枉过正!

变压器易于设置!

tsconfig

{
   compilerOptions: {
      // ....
      "plugins": [
         { "transform": "typescript-transform-macros" }
      ]
   }
}

当然还有安装软件包:

npm install typescript ttypescript typescript-transform-macros --save-dev

所以是的,设置太快了!哪里过分了!不是真的!

但关键是在大多数情况下添加一个无用的变量不会影响性能!

另一方面!使用宏可能太有趣了!包括一些好的优化!就像拥有数组迭代的宏一样!并有流畅的语法!然后将其转换为正常的for循环!因为它的性能更好!这在高迭代应用程序中很重要!和处理!

示例:

declare function MACRO<T>(t: T): T;

const MAP = MACRO(
  <T, L>(
    inputConst: T[],
    visitor: (value: T, index?: number, input?: T[]) => L
  ) => {
    const input = inputConst;
    const length = input.length;
    const result = new Array(length) as L[];
    for (let i = 0; i < length; i++) {
      result[i] = visitor(input[i], i, input);
    }
    return result;
  }
);

declare interface Array<T> {
  MAP: Array<T>["map"];
}

console.log([1, 2, 3].MAP(n => 3 * n + 1));

输出:

"use strict";
const input_1 = [1, 2, 3];
const length_1 = input_1.length;
const result_1 = new Array(length_1);
for (let i_1 = 0; i_1 < length_1; i_1++) {
  result_1[i_1] = 3 * input_1[i_1] + 1;
}
console.log(result_1);
//> [ 4, 7, 10 ]

这样的东西可以派上用场!所以喜欢的人可以试一试!

否则只需使用普通变量就可以了!使用闭包编译器之类的优化器也可以删除它!出于多种原因,这是一个不错的选择!特别是如果代码是针对浏览器的!

否则宏可能太有趣了!对于很多事情!

我更重视可能性!然后说!在大多数情况下,这无关紧要!创建一个变量!或者只是使用丑陋的铸造!对于这种无用的事情,您不需要了解很多元素!否则变压器和宏的设置太简单了!所以使用优化器!值得检查和了解!

那些是我能看到的选项!那很容易搭配!并且直观!