Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/javascript/411.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Javascript 在TypeScript中处理类型更改的副作用_Javascript_Typescript_Typescript Typings_Side Effects_Code Readability - Fatal编程技术网

Javascript 在TypeScript中处理类型更改的副作用

Javascript 在TypeScript中处理类型更改的副作用,javascript,typescript,typescript-typings,side-effects,code-readability,Javascript,Typescript,Typescript Typings,Side Effects,Code Readability,关于如何处理TypeScript中具有类型改变副作用的函数,这是一个更为开放的问题。我知道并且非常同意这样的观点,即函数应该尽可能少地产生副作用(如果有的话) 但有时,需要就地更改对象(及其类型),而不是使用其他静态类型创建对象的新副本。我经常遇到的原因是可读性、效率或减少行数 由于我最初的示例过于复杂,所以这里有一个(希望是)非常基本的示例: type KeyList = 'list' | 'of' | 'some' | 'keys'; // Original type (e.g. load

关于如何处理
TypeScript
中具有类型改变副作用的函数,这是一个更为开放的问题。我知道并且非常同意这样的观点,即函数应该尽可能少地产生副作用(如果有的话)

但有时,需要就地更改对象(及其类型),而不是使用其他静态类型创建对象的新副本。我经常遇到的原因是可读性、效率或减少行数

由于我最初的示例过于复杂,所以这里有一个(希望是)非常基本的示例:

type KeyList = 'list' | 'of' | 'some' | 'keys';

// Original type (e.g. loaded from a JSON file)
interface Mappable {
    source: { [K in KeyList]: SomeNestedObject },
    sourceOrder: KeyList[];
}

// Mapped Type (mapped for easier access)
interface Mapped {
    source: { [K in KeyList]: SomeNestedObject },
    sourceOrder: SomeDeepObject[];
}

// What I have to do to keep suggestions and strict types all the way
const json: Mappable = JSON.parse(data); // ignoring validation for now
const mapped: Mapped = toMappedData(json);

// What I would like to to
const mapped: Mappable = JSON.parse(data);
mapData(mapped); // mapped is now of type Mapped
原因,我想改变对象属性及其类型的原因可能是:

  • json
    对象非常大,如果在内存中有两个副本,则会适得其反
  • json
    对象的深层副本创建到
    mapped
    对象中非常麻烦
我不相信“我想做什么”下的代码可读性很强,不管它是否工作我要寻找的是一种干净且类型安全的方式来处理此问题。或者,在alternativley中,为解决此问题而扩展typescript功能的建议或想法。


非常感谢您对此提出的任何建议、想法和意见!也许我对这一点太深了,看不到真正简单的解决方案。

我目前的做法是:

type KeyList = 'list' | 'of' | 'some' | 'keys';

// Merged the types to keep the code a little shorter
// Also makes clear, that I don't alter the type's structure
interface MyType<M ext boolean = true> {
    source: { [K in KeyList]: SomeNestedObject },
    sourceOrder: (M ? SomeNestedObject : (KeyList | SomeNestedObject))[];
}

function mapData(data: MyType<true>): MyType {
    const { source, sourceOrder } = data.sourceOrder
    for (let i = 0; i < sourceOrder.length; i++) {
        if (typeof sourceOrder[i] == 'string') {
            sourceOrder[i] = source[sourceOrder[i]];
        }
    }

    return (data as unknown) as MyType;
}

const json: MyType<true> = JSON.parse(data);
const mapped: MyType = mapData(json);

// mapped now references json instead of being a clone
type KeyList='list'|'some'|'keys'中的'list'|';
//合并类型以使代码稍微短一点
//也很清楚,我不会改变类型的结构
接口MyType{
来源:{[K在键列表中]:SomeNestedObject},
sourceOrder:(M?SomeNestedObject:(键列表| SomeNestedObject))[];
}
函数mapData(数据:MyType):MyType{
const{source,sourceOrder}=data.sourceOrder
for(设i=0;i
我不喜欢这种方法的地方:

  • 这不是类型安全的。Typescript无法检查我是否正确更改了类型
    • 由于这个原因,我不得不转换为未知,这是不理想的
  • json
    类型没有那么严格。可能会对代码建议产生负面影响
  • 该功能具有
    副作用
    返回类型
    (只有
    副作用
    返回类型
    会更干净)

    • 我认为你想做的事是不可能的。您要求typescript更改变量类型作为副作用。但这会带来各种各样的并发症

      如果有条件地运行
      mapData
      函数会怎么样

      const mapped: Mappable = JSON.parse(data);
      if (Math.random() > 0.5) {
        mapData(mapped); // mapped is now of type Mapped
      }
      // What type is `mapped` here?
      
      或者,如果在转换对象之前传递对该对象的引用,该怎么办

      function doAsyncStuff(obj: Mappable) {}
      doAsyncStuff(mapped)
      mapData(mapped)
      // Then later, doAsyncStuff(obj) runs but `obj` is a different type than expected
      
      我认为这里最接近的是一个typeguard,它可以转换为一个中间类型,支持转换前类型和转换后类型的并集,在这里您可以实际执行转换

      interface A {
        foo: string
      }
      
      interface B {
        foo: string
        bar: string
      }
      
      interface A2B {
        foo: string
        bar?: string
      }
      
      function transform(obj: A): obj is B {
        const transitionObj: A2B = obj
        transitionObj.bar = "abc" // Mutate obj in place from type A to type B
        return true
      }
      
      const obj: A = { foo: 'foo' }
      if (transform(obj)) {
        obj // type is B in this scope
      }
      obj // but obj is still type A here, which could lead to bugs.
      
      但是如果在该条件之外实际使用
      obj
      作为类型
      A
      ,则可能会导致运行时错误,因为类型是错误的。因此,带有副作用的typeguard函数也是一个非常糟糕的主意,因为typeguard允许您覆盖TypeScript正常的键入


      我真的认为您已经在这里使用了最好的方法,尤其是当输出类型与输入类型不同时。不可变地将新对象构造为新类型

      const mapped: Mapped = toMappedData(json);
      

      如果性能或内存是一个巨大的问题,那么您可能不得不为此牺牲类型安全性。编写健壮的单元测试,将其转换为任意类型,添加关于那里发生的事情的非常显著的注释。但是,除非您一次处理数百MB的数据,否则我打赌这真的没有必要。

      您是在尝试这样做吗?(显然没有错误)什么是
      KeyList
      ,什么是
      SomeDeepObject
      ?你是说他们可以通过身份功能相互转换吗?@AlexWayne,是的。虽然在您的示例中,变换函数的类型定义隐藏了它对其参数的副作用,但这不是我想要的。我正在寻找一种方法来明确函数有一个副作用,即改变参数的类型。@Bergi KeyList是某种字符串类型,而SomeDeepObject是某种更大的对象,在JSON文件中无法反复重复。我将重命名为SomeNestedObject,并为keylist添加一个简短的定义,那么,如果不使用
      toMappedData
      函数更改
      .sourceOrder
      的值,您怎么说“mapped现在是mapped类型”呢?您可能是对的。初始化一个相当大的变量(约8k行JSON)然后立即丢弃它似乎仍然是错误的。也许把他们分开比较好,这样我就不会为他们感到难过了