Foreach with structuredClone run into RangeError - javascript

const result: Model[] = [];
array.forEach((element) => {
if (condition1)
result.push(structuredClone(element));
if (condition2)
result.push(structuredClone(element));
});
The object is nested:
export interface Model extends Dto {
indentation?: number;
previous?: Model;
next?: Model;
}
export interface Dto {
...
children?: Array<Dto>;
contentChunks?: Array<ContentChunk>;
id?: number;
...
}
If the size of array is small i works. But on big arrays it runs into
ERROR RangeError: Maximum call stack size exceeded
Suggestions to improve appreciated.
Update:
on changing structuredClone to JSON.parse(JSON.stringify
ERROR TypeError: Converting circular structure to JSON
--> starting at object with constructor 'Object'
| property 'next' -> object with constructor 'Object'
--- property 'previous' closes the circle

Related

How to delete all keys from an object in typescript

I am trying to access the keys in an object which is of type FilterType
Here is the interface -
export interface FilterType {
name?: string[];
status?: string[];
brand?: string[];
categoryAndColour?: {
[category: string]: string[];
};
rating?: string[];
}
Here is the object -
const newState: FilterType = { ...state };
I am trying to have a function which will remove all the keys from newState however whenever I try to map through the object or to a for..in I keep getting similar errors.
I am currently trying this -
for (var key in newState){
delete newState[key];
}
return newState;
But I get the error Element implicitly has an 'any' type because expression of type 'string' can't be used to index type
And No index signature with a parameter of type 'string' was found on type
How can I solve this?
Since you're trying to create a new empty state, just define the newState as a new object:
const newState: FilterType = {};
If you still want to delete all the keys anyway, define the type of key before using it in for..in (TS playground):
let key: keyof FilterType;
for (key in newState){
delete newState[key];
}
Try this:
Object.keys(newState).forEach((key) => delete newState[key]);
now, if you run:
console.log(newState)
the expected output will be an empty object:
{ }

Typescript: Having problems with mapping nested Record values wrapped in vue3 reactive proxy

UPDATED VERSION:
I have several implementations of a generic class, which is wrapped in the vue3 composition api function "reactive", which is basically a proxy which keeps track of any updates:
export declare function reactive<T extends object>(target: T): UnwrapNestedRefs<T>;
How can I implement a class so that the constructor takes an object of such implementations, and have such inner types unwrapped as parameter of a callback function which is the second argument of the constructor?
Like this:
const campaignWrapped = reactive(new MyWrapper<Campaign>(campaign));
const batchesWrapped = reactive(new MyWrapper<Batch[]>(batches));
new MyClass({ first: campaignWrapped, second: batchesWrapped }, (states) => {
states.first; // Should be of type Campaign
states.second; // Should be of type Batch[]
});
It seems like it's not possible to derive the type from the proxy object, because extends MyClass<infer U> ? ... doesn't match anymore. The only way around seems to be adding an interface to the Class and check for this interface instead. Even if I unwrap UnwrapNestedRefs first and infer it's type, I still can't get the real inner type.
OLD VERSION: (simplified)
(just for the context, as my wrong attempt might help someone to understand the difference between union and intersect - which wasn't clear to me)
I want to get the unboxed types in a callback, which have to be derived from generic types.
So far so good. It seems to work if I have only one record entry.
But as soon as there is another, it fails.
The resulting type looks ALMOST correct:
Record<"first", Campaign> | Record<"second", Batch[]>
How can I tell typescript to use intersect and not union? So the type would become:
Record<"first", Campaign> & Record<"second", Batch[]>
(BONUS QUESTION: Is it possible to get rid of the LSW interface and use the type of the class directly? For me the extends LazyStateWrapper<infer U> ? ... didn't work.)
Working example in typescriptlang.org playground here
And here is the code inline:
type UnwrapInner<X, S extends string> = X extends LSW<infer X>
? Record<S, X>
: never;
type UnwrapLazyStateWrapper<T> = T extends Record<infer S, infer U> ? (S extends string ? UnwrapInner<T[S], S> : never) : never;
export class TransientLazyState<X, T extends object> {
constructor(states: X, cb: (states: UnwrapLazyStateWrapper<X>) => T | undefined) {
console.log('todo');
}
}
interface LSW<T extends object> {
getStateClone(): T | undefined;
}
export class LazyStateWrapper<T extends object> implements LSW<T> {
private obj: T;
constructor(instance: T) {
this.obj = instance;
}
getStateClone(): T | undefined {
return {} as T;
}
}
type Campaign = { id: number; foo: string };
type Batch = { id: number; bar: string };
const campaign: Campaign = { id: 123, foo: 'x' };
const batches: Batch[] = [];
const campaignWrapped = new LazyStateWrapper(campaign);
const batchesWrapped = new LazyStateWrapper(batches);
const test1 = new TransientLazyState({ first: campaignWrapped }, (states) => {
const xy = states.first; // Yay. Works! Is of type "Campaign"
console.log(xy);
return undefined;
});
const test2 = new TransientLazyState({ first: campaignWrapped, second: batchesWrapped }, (states) => {
const xy = states.first; // Doesn't work anymore, once there are more records. But to me the type looks ALMOST correct. ERROR: Property 'first' does not exist on type 'Record<"first", Campaign> | Record<"second", Batch[]>'. Property 'first' does not exist on type 'Record<"second", Batch[]>'.(2339)
console.log(xy);
return undefined;
});

TypeScript - How do you chain accessing optional nested type properties?

I have a Client class that stores caches of other objects that the application needs to keep in memory. The structure of the object's cache is developer-defined. For example, if we have a cache of Example objects:
class Example {
property1: string;
property2: string;
}
The developer might only want property1 cached.
import { EventEmitter } from "events";
// Constructor options for `Client`
interface ClientData {
cacheStrategies?: CacheStrategies;
}
// How various objects should be cached
interface CacheStrategies {
example?: ExampleCacheStrategies;
...
}
// Metadata for how each type of object should be cached
interface ExampleCacheStrategies {
cacheFor?: number;
customCache?: ExampleCustomCacheData;
}
// The custom structure of what parts of `example` objects should be cached
interface ExampleCustomCacheData {
property1?: boolean;
property2?: boolean;
}
// The object stored in `Client.exampleCache`, based on the custom structure defined in `ExampleCustomCacheData`
interface CustomExampleData<CachedExampleProperties extends ExampleCustomCacheData> {
property1: CachedExampleProperties["property1"] extends true ? string /* 1 */ : undefined;
property2: CachedExampleProperties["property2"] extends true ? string : undefined;
}
class Client<ClientOptions extends ClientData> extends EventEmitter {
// The map's value should be based on the custom structure defined in `ExampleCustomCacheData`
exampleCache: Map<string, CustomExampleData<ClientOptions["cacheStrategies"]["example"]["customCache"]>>;
constructor(clientData: ClientOptions) {
super();
this.exampleCache = new Map();
}
}
const client = new Client({
cacheStrategies: {
example: {
/**
* The properties of `example` objects that should be cached
* This says that `property1` should be cached (string (1))
*/
customCache: {
property1: true, // (2)
... // (3)
}
}
}
});
client.exampleCache.set("123", {
property1: "value"
});
const exampleObject = client.exampleCache.get("123");
if (exampleObject) {
// Should be `string` instead of `string | undefined` (2)
console.log(exampleObject.property1);
// `string | undefined`, as expected since it's falsey (3)
console.log(exampleObject.property2);
}
As explained in the comments above the console.log()s, the goal is for objects that are pulled from the cache to have property1 be a string instead of string | undefined.
The problem is that exampleCache: Map<string, CustomExampleData<ClientOptions["cacheStrategies"]["example"]["customCache"]>>; doesn't work since both ClientOptions["cacheStrategies"] and ClientOptions["cacheStrategies"]["example"] are optional. The following doesn't work either:
exampleCache: Map<string, CustomExampleData<ClientOptions["cacheStrategies"]?.["example"]?.["customCache"]>>;
It errors with '>' expected at ?.. How can I solve this?
Syntax like the optional chaining operator ?. or the non-null assertion operator ! only applies to value expressions that will make it through to JavaScript in some form. But you need something that works with type expressions which exist only in the static type system and are erased when transpiled.
There is a NonNullable<T> utility type which is the type system analog of the non-null assertion operator. Given a union type T, the type NonNullable<T> will be the same as T but without any union members of type null or undefined:
type Foo = string | number | undefined;
type NonNullableFoo = NonNullable<Foo>;
// type NonNullableFoo = string | number
In fact, the compiler actually uses it to represent the type of an expression that has the non-null assertion operator applied to it:
function nonNullAssertion<T>(x: T) {
const nonNullX = x!;
// const nonNullX: NonNullable<T>
}
So, everywhere you have a type T that includes null or undefined and you would like to remove it, you can use NonNullable<T>. In your code, you will need to do it multiple times. In the interest of something like brevity (of code, not my explanation), let's use a shorter alias:
type NN<T> = NonNullable<T>;
and then
class Client<C extends ClientData> extends EventEmitter {
exampleCache: Map<string, CustomExampleData<
NN<NN<NN<C["cacheStrategies"]>["example"]>["customCache"]>>
>;
}
This compiles without error, and behaves how I think you'd like it:
console.log(exampleObject.property1.toUpperCase()); // string
console.log(exampleObject.property2); // undefined
Playground link to code

typescript undefined on the entire interface

How to declare the entire MyCustomObject interface can fallback to an empty object?
interface MyCustomObject {
name: string,
age: number
}
Below case is safe, it has default property of name and age as fallback, but sometime if the obj is from other source like an api, it can be some other type like an empty {} or even an empty []
const obj: MyCustomObject = {
name: "",
age: 0
}
I tried this
interface MyCustomObject {
name: string,
age: number
} | {}
it doesn't work that way.
the most correct way is to create a union
type IUnionType = MyCustomObject | {};
other ways to handle this is to make each property optional
interface MyCustomObject {
name?: string,
age?: number
}
Or create an optional object property where a union is defined in the original interface.
interface MyCustomObject2 {
obj: MyCustomObject | {}
}
examples
I have one interface having some member variables
suppose my interface name is IUser and variable name is iUser.
I have to make it undefine but I m getting error that we cannot make iUser = undefine
so I did something like this.
let obj: any;
this.iUser = obj;
obj = undefined
and it works for me

How can I create an interface for a collection of objects?

I have the following Javascript object:
{
"dataMap":{
"2027":{
"userId":2027,
"code":"abcdef",
"title":"abcdef",
"questions":1
}
"2028":{
"userId":2028,
"code":"abcdef",
"title":"abcdef",
"questions":1
}
}
}
It contains another object dataMap and inside that are other ojbects. Can someone help me by telling me how I can create an interface for the dataMap object?
What I would like is to have an interface so I can enter:
var a = b.dataMap[2027].userId // okay and allowed
var a = b.dataMap[2027].xxxyId // gives a typescript error
You can use an interface with an index signature:
interface User {
userId: number;
code: string;
title: string;
questions: number;
}
interface DataMap {
[index: number]: User;
}
And for your specific example where the data map is again contained in an object:
interface DataMapContainer {
dataMap: DataMap;
}
var b: DataMapContainer = { "dataMap": { ... } };
b.dataMap[2027].userId; // okay
b.dataMap[2027].xxx; // error
See also: TypeScript interface for object with arbitrary numeric property names?

Categories