GeezerVisonThe World Wide Web, restored to 800 pixels of glory. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Home|About|Gaming cabinet|Archives|How it works|Starter sites | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
GeezerVison is showing github.com — Add definitions for WeakRef and FinalizationRegistry by ikokostya · Pull Request #38232 · microsoft/TypeScript Conversation
Contributor
Fixes #32393 Link to proposal https://github.com/tc39/proposal-weakrefs/blob/master/reference.md
Sorry, something went wrong. Uh oh!
There was an error while loading. Please reload this page. All reactions
|
There was an error while loading. Please reload this page.
|
Great, this looks good to me:
The file naming fits too, thanks 👍 |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
|
I'll give Nathan a chance to look over it, but I'm good to merge |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
Questions about the type of target and unregisterToken:
unknown or {} instead.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
| * @param callback If given, the registry uses the given callback instead of the one it was | ||
| * created with. | ||
| */ | ||
| cleanupSome(callback?: (heldValue: any) => void): void; |
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
it's weird that heldValue: any but target: object and unregisterToken: object. I know the proposal uses the terms 'value' and 'object' but I don't think they exactly match the type system's definitions. In particular, object means "non-primitive". Can you provide null, undefined, "foo", false or 3 for target or unregisterToken?
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
target must be an object, i.e. non-primitive type.
Specification: https://tc39.es/proposal-weakrefs/#sec-weak-ref-target
- If Type(target) is not Object, throw a TypeError exception.
V8 implementation behavior:
$ node --harmony-weak-refs
Welcome to Node.js v14.0.0.
Type ".help" for more information.
> new WeakRef(null)
Uncaught TypeError: WeakRef: target must be an object
at new WeakRef (<anonymous>)
> new WeakRef(undefined)
Uncaught TypeError: WeakRef: target must be an object
at new WeakRef (<anonymous>)
> new WeakRef(1)
Uncaught TypeError: WeakRef: target must be an object
at new WeakRef (<anonymous>)
> new WeakRef("str")
Uncaught TypeError: WeakRef: target must be an object
at new WeakRef (<anonymous>)
unregisterToken must be an object (non-primitive type), or undefined.
Specification: https://tc39.es/proposal-weakrefs/#sec-finalization-registry.prototype.register
- If Type(unregisterToken) is not Object,
a. If unregisterToken is not undefined, throw a TypeError exception.
b. Set unregisterToken to empty.
V8 implementation behavior (please note that v8 in Node.js still uses old specification name FinalizationGroup instead of FinalizationRegistry):
$ node --harmony-weak-refs
> var fg = new FinalizationGroup(() => {})
undefined
> fg.register({}, null, null)
Uncaught TypeError: unregisterToken ('null') must be an object
at FinalizationGroup.register (<anonymous>)
> fg.register({}, null, undefined)
undefined
> fg.register({}, null, 1)
Uncaught TypeError: unregisterToken ('1') must be an object
at FinalizationGroup.register (<anonymous>)
> fg.register({}, null, 'a')
Uncaught TypeError: unregisterToken ('a') must be an object
at FinalizationGroup.register (<anonymous>)
> fg.register({}, null, false)
Uncaught TypeError: unregisterToken ('false') must be an object
at FinalizationGroup.register (<anonymous>)
heldValue can have any type.
Specification: https://tc39.es/proposal-weakrefs/#sec-finalization-registry.prototype.register
V8 implementation behavior:
$ node --harmony-weak-refs
> var fg = new FinalizationGroup(() => {})
undefined
> fg.register({}, null)
undefined
> fg.register({}, false)
undefined
> fg.register({}, 1)
undefined
> fg.register({}, {})
undefined
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
I'd suggest omitting cleanupSome from .d.ts, or otherwise somehow mark it as dispreferred/not tab-complete it. It's "normative optional" in the specification, it's currently under some debate, and it's not going to be part of what V8/Chrome ships initially. See whatwg/html#5446 for context.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
If it's "normative optional" in the specification, it can be marked as optional in .d.ts. Anyway this declaration is available only under --lib esnext compiler flag, which means that it isn't stable. If in final stage of the proposal this method will be removed, corresponding TypeScript declaration should reflect this change before move to stable es2020 lib.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
If Chrome's not going to ship with it initially, let's make this optional to start so that people have to prove that it's available before using it. (Only when strict: true of course.)
| cleanupSome(callback?: (heldValue: any) => void): void; | |
| cleanupSome?(callback?: (heldValue: any) => void): void; |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
|
Actually this behavior is the same as in TypeScript/lib/lib.es2015.collection.d.ts Line 45 in 0326534
|
Sorry, something went wrong.
There was an error while loading. Please reload this page.
| * @param callback If given, the registry uses the given callback instead of the one it was | ||
| * created with. | ||
| */ | ||
| cleanupSome(callback?: (heldValue: any) => void): void; |
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
If Chrome's not going to ship with it initially, let's make this optional to start so that people have to prove that it's available before using it. (Only when strict: true of course.)
| cleanupSome(callback?: (heldValue: any) => void): void; | |
| cleanupSome?(callback?: (heldValue: any) => void): void; |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
|
Is there any reason |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
|
cleanupSome has been moved into a separate Stage 2 proposal, so I recommend omitting it from the typings here. |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
What would you include in the Generic?
I see a lot more value in the latter two, but in many cases you might only use one or the other. It doesn't seem obvious to me what the Generic implementation would be. |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
I've generally defined it with all 3 as: declare global { class FinalizationRegistry< TValue extends object=object, THeldValue=any, TUnregisterToken extends object=never > { constructor( finalizationCallback: (heldValue: THeldValue) => void, ); register( object: TValue, heldValue: THeldValue, unregisterToken?: TUnregisterToken, ): void; unregister(unregisterToken: TUnregisterToken): void; } } I do agree you'd often only use some, it's a bit of a shame there aren't named generics. There are hacky ways around it (like this) but this isn't an existing convention in the TS standard lib. type FinalizationOptions = { Value: object, HeldValue: any, UnregisterToken: object, } type Get<T extends object, K, Else> = K extends keyof T ? T[K] : Else; declare class FinalizationRegistry<T extends Partial<FinalizationOptions>> { constructor( finalizationCallback: (heldValue: Get<T, 'HeldValue', any>) => void, ); register( value: Get<T, 'Value', object>, heldValue: Get<T, 'HeldValue', any>, unregisterToken?: Get<T, 'UnregisterToken', never>, ): void; unregister( unregisterToken: Get<T, 'UnregisterToken', never>, ): void; } const f = new FinalizationRegistry<{ HeldValue: number }>((i /* number */) => { console.log(i); }); f.register({} /* object */, 3 /* number */); |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
| deref(): T | undefined; | ||
| } | ||
|
|
||
| interface WeakRefConstructor { |
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
Hi, any chance to expedite this change?
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
Yes, it got lost in my email, but I think it's good to go after I re-run the github CI.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
|
Funny coincidence, i just started using weak refs/Finalisation registry in a project yesterday and was little annoyed that it wasn't typed (the typescript nightly i had installed was a few days old). Perfect timing. Two notes on FinalisationRegistry: As for Generics, this is what i used:
declare class FinalizationRegistry<T extends object, V = void, Token extends object = object> { constructor (callback: (heldValue: V) => void) register (target: T, heldValue: V, token?: Token): void unregister (token: Token): void } usage: const registry = new FinalizationRegistry<Record<string, string>>(() => console.log('some cleanup optimisation')) // => `FinalizationRegistry<Record<string, string>, void, object>` const registry = new FinalizationRegistry<readonly string[], number, readonly string[]>((heldItem: number) => console.log(`Array of ${heldItem} hasn't been unregistered`')) const arr = [1,2,3,4] as const registry.register(arr, arr.length, arr) class Collection<T extends object, Held = void> { #registry: FinalizationRegistry<T, Held, this> constructor (callback: (heldValue: Held) => void, iterable?: Iterable<[T, Held]>) { this.#registry = new (FinalizationRegistry)(callback) for (const [item, held] of iterable ?? []) this.#registry.register(item, held, this) } register (item: T, heldValue: Held) { this.#registry.register(item, heldValue, this) } reset () { this.#registry.unregister(this) } } How James Browning (@Jamesernator) did the |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
| * Creates a WeakRef instance for the given target object. | ||
| * @param target The target object for the WeakRef instance. | ||
| */ | ||
| new<T extends object>(target?: T): WeakRef<T>; |
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
It’s invalid to call WeakRef with undefined:
| new<T extends object>(target?: T): WeakRef<T>; | |
| new<T extends object>(target: T): WeakRef<T>; |
I’m fixing this in #42274.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
|
|
||
| declare var WeakRef: WeakRefConstructor; | ||
|
|
||
| interface FinalizationRegistry { |
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
| interface FinalizationRegistry { | |
| interface FinalizationRegistry<T> { |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
| * object. If provided (and not undefined), this must be an object. If not provided, the target | ||
| * cannot be unregistered. | ||
| */ | ||
| register(target: object, heldValue: any, unregisterToken?: object): void; |
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
| register(target: object, heldValue: any, unregisterToken?: object): void; | |
| register(target: object, heldValue: T, unregisterToken?: object): void; |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
| } | ||
|
|
||
| interface FinalizationRegistryConstructor { | ||
| readonly prototype: FinalizationRegistry; |
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
| readonly prototype: FinalizationRegistry; | |
| readonly prototype: FinalizationRegistry<any>; |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
| * Creates a finalization registry with an associated cleanup callback | ||
| * @param cleanupCallback The callback to call after an object in the registry has been reclaimed. | ||
| */ | ||
| new(cleanupCallback: (heldValue: any) => void): FinalizationRegistry; |
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
| new(cleanupCallback: (heldValue: any) => void): FinalizationRegistry; | |
| new<T>(cleanupCallback: (heldValue: T) => void): FinalizationRegistry<T>; |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
Successfully merging this pull request may close these issues.
Suport for WeakRef / FinalizationGroup
There was an error while loading. Please reload this page.