Skip to content

Make structuredClone generic #1520

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged

Conversation

ConorLinehan
Copy link
Contributor

Looks to addresse this issue.

Creates a generic for structuredClone.

Playground with examples

Addressed from issue

Typed return value

Types return value now instead of any. (Taken from test)

function assertType<T>(_x: T) {}

const toBeCloned = {
  name: "abc",
  address: "test",
  age: 30,
  info: {
    url: "https://example.com",
  },
} as const;

const nonMatchingType = { foo: "bar" } as const;
const clone = structuredClone(toBeCloned);

assertType<typeof toBeCloned>(clone);
// @ts-expect-error non matching type
assertType<typeof nonMatchingType>(clone);

Make readonly types mutable

type Mutable<T> = {
  -readonly [P in keyof T]: T[P];
};

type readonlyExample = Readonly<{
  name: string;
  age: number;
}>

const example: readonlyExample = {
  name: 'test',
  age: 30
};

const clone = window.structuredClone(example);
// @ts-expect-error non mutable
clone.age = 29;

const mutableClone = window.structuredClone<Mutable<readonlyExample>>(clone);
mutableClone.age = 29;

instead of being within the generic function can pass through mutable helper.

Not addressed from issue

Error on non structured-cloneable types

From mdn docs will throw exception if not structuredCloneable type. Could add a generic constraint to a StructuredCloneable type to prevent these errors in this pr?

Example

const nonSeriazable = {
  func: () => {}
}

// @ts-expect-error Not serializable
const errorClone = window.structuredClone(nonSeriazable); // Will error on runtime

Errors on transferring an object.

From docs

When an object is transferred using it in the original throws an exception, which isn't captured on typing.

Example

// Create an ArrayBuffer with a size in bytes
const buffer1 = new ArrayBuffer(16);

const object1 = {
  buffer: buffer1,
};

// Clone the object containing the buffer, and transfer it
const object2 = structuredClone(object1, { transfer: [buffer1] });

// Create an array from the cloned buffer
const int32View2 = new Int32Array(object2.buffer);
int32View2[0] = 42;
console.log(int32View2[0]);

// @ts-expect-error detached ArrayBuffer
const int32View1 = new Int32Array(object1.buffer); // Will error on runtime

Not sure on handling this one buffer1 is still an ArrayBuffer after being cloned, it'll just throw an error if used. Maybe doesn't need to be handled here.

@github-actions
Copy link
Contributor

Thanks for the PR!

This section of the codebase is owned by @saschanaz - if they write a comment saying "LGTM" then it will be merged.

@ConorLinehan
Copy link
Contributor Author

@microsoft-github-policy-service agree

@saschanaz
Copy link
Contributor

I guess correctly limiting the type would be a hard work if not impossible. This is already better than before, so I'll merge it. Thanks!

LGTM

@github-actions github-actions bot merged commit 8e5f5db into microsoft:main Mar 12, 2023
@github-actions
Copy link
Contributor

Merging because @saschanaz is a code-owner of all the changes - thanks!

@ConorLinehan
Copy link
Contributor Author

@saschanaz I'll have a look to see can it work in a followup pr, think an extends on a mapped type with Primitives and Transfers should handle it. Think your right and we'll be able to catch the transferred over case though

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants