TypeScript: Recursive Deep Mutable with Generics. Error: T is not assignable to Mutable<T>
TypeScript: Recursive Deep Mutable with Generics. Error: T is not assignable to Mutable<T>
我正在尝试编写深度递归 Mutable
类型:
Mutable<T> = ...
// remove all "readonly" modifiers
// convert all ReadonlyArray to Array
// etc.
// and do it all recursively
const mutate = <T>(val: T, mutateFn: (mutableVal: Mutable<T>) => void): T => {
return null as any
}
只要我不使用泛型就可以正常工作:
// works fine
mutate([] as ReadonlyArray<string>, mutableVal => {
mutableVal.splice(42, 0, "test");
});
但是在通用函数中使用它时出现错误:
// Error: Argument of type 'T' is not assignable to parameter of type 'Mutable<T>'.
const doSomething = <T>(array: ReadonlyArray<T>, index: number, elem: T) => {
mutate(array, mutableVal => {
// Here's the error
// v
mutableVal.splice(index, 0, elem);
});
}
我知道,可变数组的类型是 Array<Mutable<T>>
,splice
现在需要一个 Mutable<T>
值,而不是 T
。但是我不知道怎么解决。
你知道如何解决这个问题吗?
我创建了一个 TypeScript Playground,因此您可以玩代码:
Link to TypeScript Playground
我的建议是这样做:
const doSomething = <T>(array: ReadonlyArray<T>, index: number, elem: T) => {
mutate({ array: array, elem: elem }, mutableVal => {
mutableVal.array.splice(index, 0, mutableVal.elem);
});
}
想法是您需要 elem
可变才能将其添加到深度可变数组中,但您的原始调用并没有这样做。因为你想同时改变 array
和可能的 elem
,最直接的解决方案是传入一个同时包含 array
和 elem
的对象,然后对 deep-mutable 进行操作那个版本。
只有您知道在 elem
和 array
上调用 mutate()
是否可以接受,因为 mutate()
的实现被排除在外。我猜它会涉及这样的断言:
const mutate = <T>(val: T, mutateFn: (mutableVal: Mutable<T>) => void): T => {
mutateFn(val as Mutable<T>); //♀️
return val;
}
在这种情况下,我会说 "who cares" 你是在 elem
上调用 mutate()
,还是只是将 elem
断言到 [=24] 中的可变对象=].另一方面,如果您的实现涉及克隆,那么您应该考虑在 elem
上调用它是否有意义。
好的,希望对您有所帮助。祝你好运!
使用来自 utility-types 的 DeepNonNullable 作为灵感和我想出的 99% 的代码:
import { Mutable } from 'utility-types';
export type DeepMutable<T> = T extends (...args: any[]) => any
? T
: T extends any[]
? DeepMutableArray<T[number]>
: T extends object
? DeepMutableObject<T>
: T;
/** @private */
export type DeepMutableArray<T> = Array<DeepMutable<Mutable<T>>>;
/** @private */
export declare type DeepMutableObject<T> = {
[P in keyof T]-?: DeepMutable<Mutable<T[P]>>;
};
我正在尝试编写深度递归 Mutable
类型:
Mutable<T> = ...
// remove all "readonly" modifiers
// convert all ReadonlyArray to Array
// etc.
// and do it all recursively
const mutate = <T>(val: T, mutateFn: (mutableVal: Mutable<T>) => void): T => {
return null as any
}
只要我不使用泛型就可以正常工作:
// works fine
mutate([] as ReadonlyArray<string>, mutableVal => {
mutableVal.splice(42, 0, "test");
});
但是在通用函数中使用它时出现错误:
// Error: Argument of type 'T' is not assignable to parameter of type 'Mutable<T>'.
const doSomething = <T>(array: ReadonlyArray<T>, index: number, elem: T) => {
mutate(array, mutableVal => {
// Here's the error
// v
mutableVal.splice(index, 0, elem);
});
}
我知道,可变数组的类型是 Array<Mutable<T>>
,splice
现在需要一个 Mutable<T>
值,而不是 T
。但是我不知道怎么解决。
你知道如何解决这个问题吗?
我创建了一个 TypeScript Playground,因此您可以玩代码: Link to TypeScript Playground
我的建议是这样做:
const doSomething = <T>(array: ReadonlyArray<T>, index: number, elem: T) => {
mutate({ array: array, elem: elem }, mutableVal => {
mutableVal.array.splice(index, 0, mutableVal.elem);
});
}
想法是您需要 elem
可变才能将其添加到深度可变数组中,但您的原始调用并没有这样做。因为你想同时改变 array
和可能的 elem
,最直接的解决方案是传入一个同时包含 array
和 elem
的对象,然后对 deep-mutable 进行操作那个版本。
只有您知道在 elem
和 array
上调用 mutate()
是否可以接受,因为 mutate()
的实现被排除在外。我猜它会涉及这样的断言:
const mutate = <T>(val: T, mutateFn: (mutableVal: Mutable<T>) => void): T => {
mutateFn(val as Mutable<T>); //♀️
return val;
}
在这种情况下,我会说 "who cares" 你是在 elem
上调用 mutate()
,还是只是将 elem
断言到 [=24] 中的可变对象=].另一方面,如果您的实现涉及克隆,那么您应该考虑在 elem
上调用它是否有意义。
好的,希望对您有所帮助。祝你好运!
使用来自 utility-types 的 DeepNonNullable 作为灵感和我想出的 99% 的代码:
import { Mutable } from 'utility-types';
export type DeepMutable<T> = T extends (...args: any[]) => any
? T
: T extends any[]
? DeepMutableArray<T[number]>
: T extends object
? DeepMutableObject<T>
: T;
/** @private */
export type DeepMutableArray<T> = Array<DeepMutable<Mutable<T>>>;
/** @private */
export declare type DeepMutableObject<T> = {
[P in keyof T]-?: DeepMutable<Mutable<T[P]>>;
};