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
非常感谢您对此提出的任何建议、想法和意见!也许我对这一点太深了,看不到真正简单的解决方案。我目前的做法是:
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)然后立即丢弃它似乎仍然是错误的。也许把他们分开比较好,这样我就不会为他们感到难过了