Skip to content

Commit 094e96a

Browse files
authored
Introduces IObservableWithChange so that typescript does not show the default type for TChange in hovers. (microsoft#236629)
* Introduces IObservableWithChange so that typescript does not show the default type for TChange in hovers. This should make it easier to understand types when observables (potentially nested) are involved. * Fixes monaco editor
1 parent c55b2da commit 094e96a

File tree

19 files changed

+73
-66
lines changed

19 files changed

+73
-66
lines changed

build/monaco/monaco.usage.recipe

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,6 @@ import * as editorAPI from './vs/editor/editor.api';
3535
a = editorAPI.editor;
3636
a = editorAPI.languages;
3737

38-
const o: IObservable<number, number> = null!;
38+
const o: IObservable<number> = null!;
3939
o.TChange;
4040
})();

src/vs/base/common/event.ts

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import { onUnexpectedError } from './errors.js';
99
import { createSingleCallFunction } from './functional.js';
1010
import { combinedDisposable, Disposable, DisposableMap, DisposableStore, IDisposable, toDisposable } from './lifecycle.js';
1111
import { LinkedList } from './linkedList.js';
12-
import { IObservable, IObserver } from './observable.js';
12+
import { IObservable, IObservableWithChange, IObserver } from './observable.js';
1313
import { StopWatch } from './stopwatch.js';
1414
import { MicrotaskDelay } from './symbols.js';
1515

@@ -666,7 +666,7 @@ export namespace Event {
666666
private _counter = 0;
667667
private _hasChanged = false;
668668

669-
constructor(readonly _observable: IObservable<T, any>, store: DisposableStore | undefined) {
669+
constructor(readonly _observable: IObservable<T>, store: DisposableStore | undefined) {
670670
const options: EmitterOptions = {
671671
onWillAddFirstListener: () => {
672672
_observable.addObserver(this);
@@ -687,21 +687,21 @@ export namespace Event {
687687
}
688688
}
689689

690-
beginUpdate<T>(_observable: IObservable<T, void>): void {
690+
beginUpdate<T>(_observable: IObservable<T>): void {
691691
// assert(_observable === this.obs);
692692
this._counter++;
693693
}
694694

695-
handlePossibleChange<T>(_observable: IObservable<T, unknown>): void {
695+
handlePossibleChange<T>(_observable: IObservable<T>): void {
696696
// assert(_observable === this.obs);
697697
}
698698

699-
handleChange<T, TChange>(_observable: IObservable<T, TChange>, _change: TChange): void {
699+
handleChange<T, TChange>(_observable: IObservableWithChange<T, TChange>, _change: TChange): void {
700700
// assert(_observable === this.obs);
701701
this._hasChanged = true;
702702
}
703703

704-
endUpdate<T>(_observable: IObservable<T, void>): void {
704+
endUpdate<T>(_observable: IObservable<T>): void {
705705
// assert(_observable === this.obs);
706706
this._counter--;
707707
if (this._counter === 0) {
@@ -718,15 +718,15 @@ export namespace Event {
718718
* Creates an event emitter that is fired when the observable changes.
719719
* Each listeners subscribes to the emitter.
720720
*/
721-
export function fromObservable<T>(obs: IObservable<T, any>, store?: DisposableStore): Event<T> {
721+
export function fromObservable<T>(obs: IObservable<T>, store?: DisposableStore): Event<T> {
722722
const observer = new EmitterObserver(obs, store);
723723
return observer.emitter.event;
724724
}
725725

726726
/**
727727
* Each listener is attached to the observable directly.
728728
*/
729-
export function fromObservableLight(observable: IObservable<any>): Event<void> {
729+
export function fromObservableLight(observable: IObservable<unknown>): Event<void> {
730730
return (listener, thisArgs, disposables) => {
731731
let count = 0;
732732
let didChange = false;

src/vs/base/common/observableInternal/autorun.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
* Licensed under the MIT License. See License.txt in the project root for license information.
44
*--------------------------------------------------------------------------------------------*/
55

6-
import { IChangeContext, IObservable, IObserver, IReader } from './base.js';
6+
import { IChangeContext, IObservable, IObservableWithChange, IObserver, IReader } from './base.js';
77
import { DebugNameData, IDebugNameData } from './debugName.js';
88
import { assertFn, BugIndicatingError, DisposableStore, IDisposable, markAsDisposed, onBugIndicatingError, toDisposable, trackDisposable } from './commonFacade/deps.js';
99
import { getLogger } from './logging.js';
@@ -286,7 +286,7 @@ export class AutorunObserver<TChangeSummary = any> implements IObserver, IReader
286286
}
287287
}
288288

289-
public handleChange<T, TChange>(observable: IObservable<T, TChange>, change: TChange): void {
289+
public handleChange<T, TChange>(observable: IObservableWithChange<T, TChange>, change: TChange): void {
290290
if (this.dependencies.has(observable) && !this.dependenciesToBeRemoved.has(observable)) {
291291
try {
292292
const shouldReact = this._handleChange ? this._handleChange({

src/vs/base/common/observableInternal/base.ts

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,13 @@ import type { derivedOpts } from './derived.js';
99
import { getLogger, logObservable } from './logging.js';
1010
import { keepObserved, recomputeInitiallyAndOnChange } from './utils.js';
1111

12+
/**
13+
* Represents an observable value.
14+
*
15+
* @template T The type of the values the observable can hold.
16+
*/
17+
export interface IObservable<T> extends IObservableWithChange<T, unknown> { }
18+
1219
/**
1320
* Represents an observable value.
1421
*
@@ -18,7 +25,7 @@ import { keepObserved, recomputeInitiallyAndOnChange } from './utils.js';
1825
* While observers can miss temporary values of an observable,
1926
* they will receive all change values (as long as they are subscribed)!
2027
*/
21-
export interface IObservable<T, TChange = unknown> {
28+
export interface IObservableWithChange<T, TChange = unknown> {
2229
/**
2330
* Returns the current value.
2431
*
@@ -71,7 +78,7 @@ export interface IObservable<T, TChange = unknown> {
7178
* ONLY FOR DEBUGGING!
7279
* Logs computations of this derived.
7380
*/
74-
log(): IObservable<T, TChange>;
81+
log(): IObservableWithChange<T, TChange>;
7582

7683
/**
7784
* Makes sure this value is computed eagerly.
@@ -98,7 +105,7 @@ export interface IReader {
98105
/**
99106
* Reads the value of an observable and subscribes to it.
100107
*/
101-
readObservable<T>(observable: IObservable<T, any>): T;
108+
readObservable<T>(observable: IObservableWithChange<T, any>): T;
102109
}
103110

104111
/**
@@ -143,7 +150,7 @@ export interface IObserver {
143150
*
144151
* @param change Indicates how or why the value changed.
145152
*/
146-
handleChange<T, TChange>(observable: IObservable<T, TChange>, change: TChange): void;
153+
handleChange<T, TChange>(observable: IObservableWithChange<T, TChange>, change: TChange): void;
147154
}
148155

149156
export interface ISettable<T, TChange = void> {
@@ -162,7 +169,7 @@ export interface ITransaction {
162169
* Calls {@link Observer.beginUpdate} immediately
163170
* and {@link Observer.endUpdate} when the transaction ends.
164171
*/
165-
updateObserver(observer: IObserver, observable: IObservable<any, any>): void;
172+
updateObserver(observer: IObserver, observable: IObservableWithChange<any, any>): void;
166173
}
167174

168175
let _recomputeInitiallyAndOnChange: typeof recomputeInitiallyAndOnChange;
@@ -185,7 +192,7 @@ export function _setDerivedOpts(derived: typeof _derived) {
185192
_derived = derived;
186193
}
187194

188-
export abstract class ConvenientObservable<T, TChange> implements IObservable<T, TChange> {
195+
export abstract class ConvenientObservable<T, TChange> implements IObservableWithChange<T, TChange> {
189196
get TChange(): TChange { return null!; }
190197

191198
public abstract get(): T;
@@ -239,7 +246,7 @@ export abstract class ConvenientObservable<T, TChange> implements IObservable<T,
239246
);
240247
}
241248

242-
public log(): IObservable<T, TChange> {
249+
public log(): IObservableWithChange<T, TChange> {
243250
logObservable(this);
244251
return this;
245252
}
@@ -248,7 +255,7 @@ export abstract class ConvenientObservable<T, TChange> implements IObservable<T,
248255
* @sealed
249256
* Converts an observable of an observable value into a direct observable of the value.
250257
*/
251-
public flatten<TNew>(this: IObservable<IObservable<TNew, any>>): IObservable<TNew, unknown> {
258+
public flatten<TNew>(this: IObservable<IObservableWithChange<TNew, any>>): IObservable<TNew> {
252259
return _derived(
253260
{
254261
owner: undefined,
@@ -390,7 +397,7 @@ export class TransactionImpl implements ITransaction {
390397
/**
391398
* A settable observable.
392399
*/
393-
export interface ISettableObservable<T, TChange = void> extends IObservable<T, TChange>, ISettable<T, TChange> {
400+
export interface ISettableObservable<T, TChange = void> extends IObservableWithChange<T, TChange>, ISettable<T, TChange> {
394401
}
395402

396403
/**
@@ -505,11 +512,11 @@ export interface IChangeTracker {
505512
}
506513

507514
export interface IChangeContext {
508-
readonly changedObservable: IObservable<any, any>;
515+
readonly changedObservable: IObservableWithChange<any, any>;
509516
readonly change: unknown;
510517

511518
/**
512519
* Returns if the given observable caused the change.
513520
*/
514-
didChange<T, TChange>(observable: IObservable<T, TChange>): this is { change: TChange };
521+
didChange<T, TChange>(observable: IObservableWithChange<T, TChange>): this is { change: TChange };
515522
}

src/vs/base/common/observableInternal/derived.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
* Licensed under the MIT License. See License.txt in the project root for license information.
44
*--------------------------------------------------------------------------------------------*/
55

6-
import { BaseObservable, IChangeContext, IObservable, IObserver, IReader, ISettableObservable, ITransaction, _setDerivedOpts, } from './base.js';
6+
import { BaseObservable, IChangeContext, IObservable, IObservableWithChange, IObserver, IReader, ISettableObservable, ITransaction, _setDerivedOpts, } from './base.js';
77
import { DebugNameData, DebugOwner, IDebugNameData } from './debugName.js';
88
import { BugIndicatingError, DisposableStore, EqualityComparer, IDisposable, assertFn, onBugIndicatingError, strictEquals } from './commonFacade/deps.js';
99
import { getLogger } from './logging.js';
@@ -386,7 +386,7 @@ export class Derived<T, TChangeSummary = any> extends BaseObservable<T, void> im
386386
assertFn(() => this.updateCount >= 0);
387387
}
388388

389-
public handlePossibleChange<T>(observable: IObservable<T, unknown>): void {
389+
public handlePossibleChange<T>(observable: IObservable<T>): void {
390390
// In all other states, observers already know that we might have changed.
391391
if (this.state === DerivedState.upToDate && this.dependencies.has(observable) && !this.dependenciesToBeRemoved.has(observable)) {
392392
this.state = DerivedState.dependenciesMightHaveChanged;
@@ -396,7 +396,7 @@ export class Derived<T, TChangeSummary = any> extends BaseObservable<T, void> im
396396
}
397397
}
398398

399-
public handleChange<T, TChange>(observable: IObservable<T, TChange>, change: TChange): void {
399+
public handleChange<T, TChange>(observable: IObservableWithChange<T, TChange>, change: TChange): void {
400400
if (this.dependencies.has(observable) && !this.dependenciesToBeRemoved.has(observable)) {
401401
let shouldReact = false;
402402
try {
@@ -460,7 +460,7 @@ export class Derived<T, TChangeSummary = any> extends BaseObservable<T, void> im
460460
super.removeObserver(observer);
461461
}
462462

463-
public override log(): IObservable<T, void> {
463+
public override log(): IObservableWithChange<T, void> {
464464
if (!getLogger()) {
465465
super.log();
466466
getLogger()?.handleDerivedCreated(this);

src/vs/base/common/observableInternal/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
export { observableValueOpts } from './api.js';
99
export { autorun, autorunDelta, autorunHandleChanges, autorunOpts, autorunWithStore, autorunWithStoreHandleChanges } from './autorun.js';
10-
export { asyncTransaction, disposableObservableValue, globalTransaction, observableValue, subtransaction, transaction, TransactionImpl, type IChangeContext, type IChangeTracker, type IObservable, type IObserver, type IReader, type ISettable, type ISettableObservable, type ITransaction, } from './base.js';
10+
export { asyncTransaction, disposableObservableValue, globalTransaction, observableValue, subtransaction, transaction, TransactionImpl, type IChangeContext, type IChangeTracker, type IObservable, type IObservableWithChange, type IObserver, type IReader, type ISettable, type ISettableObservable, type ITransaction, } from './base.js';
1111
export { derived, derivedDisposable, derivedHandleChanges, derivedOpts, derivedWithSetter, derivedWithStore } from './derived.js';
1212
export { ObservableLazy, ObservableLazyPromise, ObservablePromise, PromiseResult, } from './promise.js';
1313
export { derivedWithCancellationToken, waitForState } from './utilsCancellation.js';

src/vs/base/common/observableInternal/logging.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ interface IChangeInformation {
3939
}
4040

4141
export interface IObservableLogger {
42-
handleObservableChanged(observable: IObservable<any, any>, info: IChangeInformation): void;
42+
handleObservableChanged(observable: IObservable<any>, info: IChangeInformation): void;
4343
handleFromEventObservableTriggered(observable: FromEventObservable<any, any>, info: IChangeInformation): void;
4444

4545
handleAutorunCreated(autorun: AutorunObserver): void;
@@ -101,7 +101,7 @@ export class ConsoleObservableLogger implements IObservableLogger {
101101
: [normalText(` (unchanged)`)];
102102
}
103103

104-
handleObservableChanged(observable: IObservable<unknown, unknown>, info: IChangeInformation): void {
104+
handleObservableChanged(observable: IObservable<unknown>, info: IChangeInformation): void {
105105
if (!this._isIncluded(observable)) { return; }
106106
console.log(...this.textToConsoleArgs([
107107
formatKind('observable value changed'),
@@ -110,9 +110,9 @@ export class ConsoleObservableLogger implements IObservableLogger {
110110
]));
111111
}
112112

113-
private readonly changedObservablesSets = new WeakMap<object, Set<IObservable<any, any>>>();
113+
private readonly changedObservablesSets = new WeakMap<object, Set<IObservable<any>>>();
114114

115-
formatChanges(changes: Set<IObservable<any, any>>): ConsoleText | undefined {
115+
formatChanges(changes: Set<IObservable<any>>): ConsoleText | undefined {
116116
if (changes.size === 0) {
117117
return undefined;
118118
}

src/vs/base/common/observableInternal/utils.ts

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
*--------------------------------------------------------------------------------------------*/
55

66
import { autorun, autorunOpts, autorunWithStoreHandleChanges } from './autorun.js';
7-
import { BaseObservable, ConvenientObservable, IObservable, IObserver, IReader, ITransaction, _setKeepObserved, _setRecomputeInitiallyAndOnChange, observableValue, subtransaction, transaction } from './base.js';
7+
import { BaseObservable, ConvenientObservable, IObservable, IObservableWithChange, IObserver, IReader, ITransaction, _setKeepObserved, _setRecomputeInitiallyAndOnChange, observableValue, subtransaction, transaction } from './base.js';
88
import { DebugNameData, DebugOwner, IDebugNameData, getDebugName, } from './debugName.js';
99
import { BugIndicatingError, DisposableStore, EqualityComparer, Event, IDisposable, IValueWithChangeEvent, strictEquals, toDisposable } from './commonFacade/deps.js';
1010
import { derived, derivedOpts } from './derived.js';
@@ -259,7 +259,7 @@ export function observableSignal<TDelta = void>(debugNameOrOwner: string | objec
259259
}
260260
}
261261

262-
export interface IObservableSignal<TChange> extends IObservable<void, TChange> {
262+
export interface IObservableSignal<TChange> extends IObservableWithChange<void, TChange> {
263263
trigger(tx: ITransaction | undefined, change: TChange): void;
264264
}
265265

@@ -434,11 +434,11 @@ export class KeepAliveObserver implements IObserver {
434434
private readonly _handleValue: ((value: any) => void) | undefined,
435435
) { }
436436

437-
beginUpdate<T>(observable: IObservable<T, void>): void {
437+
beginUpdate<T>(observable: IObservable<T>): void {
438438
this._counter++;
439439
}
440440

441-
endUpdate<T>(observable: IObservable<T, void>): void {
441+
endUpdate<T>(observable: IObservable<T>): void {
442442
this._counter--;
443443
if (this._counter === 0 && this._forceRecompute) {
444444
if (this._handleValue) {
@@ -449,11 +449,11 @@ export class KeepAliveObserver implements IObserver {
449449
}
450450
}
451451

452-
handlePossibleChange<T>(observable: IObservable<T, unknown>): void {
452+
handlePossibleChange<T>(observable: IObservable<T>): void {
453453
// NO OP
454454
}
455455

456-
handleChange<T, TChange>(observable: IObservable<T, TChange>, change: TChange): void {
456+
handleChange<T, TChange>(observable: IObservableWithChange<T, TChange>, change: TChange): void {
457457
// NO OP
458458
}
459459
}
@@ -625,7 +625,7 @@ export function derivedConstOnceDefined<T>(owner: DebugOwner, fn: (reader: IRead
625625

626626
type RemoveUndefined<T> = T extends undefined ? never : T;
627627

628-
export function runOnChange<T, TChange>(observable: IObservable<T, TChange>, cb: (value: T, previousValue: undefined | T, deltas: RemoveUndefined<TChange>[]) => void): IDisposable {
628+
export function runOnChange<T, TChange>(observable: IObservableWithChange<T, TChange>, cb: (value: T, previousValue: undefined | T, deltas: RemoveUndefined<TChange>[]) => void): IDisposable {
629629
let _previousValue: T | undefined;
630630
return autorunWithStoreHandleChanges({
631631
createEmptyChangeSummary: () => ({ deltas: [] as RemoveUndefined<TChange>[], didChange: false }),
@@ -649,7 +649,7 @@ export function runOnChange<T, TChange>(observable: IObservable<T, TChange>, cb:
649649
});
650650
}
651651

652-
export function runOnChangeWithStore<T, TChange>(observable: IObservable<T, TChange>, cb: (value: T, previousValue: undefined | T, deltas: RemoveUndefined<TChange>[], store: DisposableStore) => void): IDisposable {
652+
export function runOnChangeWithStore<T, TChange>(observable: IObservableWithChange<T, TChange>, cb: (value: T, previousValue: undefined | T, deltas: RemoveUndefined<TChange>[], store: DisposableStore) => void): IDisposable {
653653
const store = new DisposableStore();
654654
const disposable = runOnChange(observable, (value, previousValue: undefined | T, deltas) => {
655655
store.clear();

src/vs/base/test/common/observable.test.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import { setUnexpectedErrorHandler } from '../../common/errors.js';
88
import { Emitter, Event } from '../../common/event.js';
99
import { DisposableStore } from '../../common/lifecycle.js';
1010
import { autorun, autorunHandleChanges, derived, derivedDisposable, IObservable, IObserver, ISettableObservable, ITransaction, keepObserved, observableFromEvent, observableSignal, observableValue, transaction, waitForState } from '../../common/observable.js';
11-
import { BaseObservable } from '../../common/observableInternal/base.js';
11+
import { BaseObservable, IObservableWithChange } from '../../common/observableInternal/base.js';
1212
import { ensureNoDisposablesAreLeakedInTestSuite } from './utils.js';
1313

1414
suite('observables', () => {
@@ -1486,18 +1486,18 @@ export class LoggingObserver implements IObserver {
14861486
constructor(public readonly debugName: string, private readonly log: Log) {
14871487
}
14881488

1489-
beginUpdate<T>(observable: IObservable<T, void>): void {
1489+
beginUpdate<T>(observable: IObservable<T>): void {
14901490
this.count++;
14911491
this.log.log(`${this.debugName}.beginUpdate (count ${this.count})`);
14921492
}
1493-
endUpdate<T>(observable: IObservable<T, void>): void {
1493+
endUpdate<T>(observable: IObservable<T>): void {
14941494
this.log.log(`${this.debugName}.endUpdate (count ${this.count})`);
14951495
this.count--;
14961496
}
1497-
handleChange<T, TChange>(observable: IObservable<T, TChange>, change: TChange): void {
1497+
handleChange<T, TChange>(observable: IObservableWithChange<T, TChange>, change: TChange): void {
14981498
this.log.log(`${this.debugName}.handleChange (count ${this.count})`);
14991499
}
1500-
handlePossibleChange<T>(observable: IObservable<T, unknown>): void {
1500+
handlePossibleChange<T>(observable: IObservable<T>): void {
15011501
this.log.log(`${this.debugName}.handlePossibleChange`);
15021502
}
15031503
}

src/vs/editor/browser/observableCodeEditor.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
import { equalsIfDefined, itemsEquals } from '../../base/common/equals.js';
77
import { Disposable, DisposableStore, IDisposable, toDisposable } from '../../base/common/lifecycle.js';
8-
import { IObservable, ITransaction, TransactionImpl, autorun, autorunOpts, derived, derivedOpts, derivedWithSetter, observableFromEvent, observableSignal, observableValue, observableValueOpts } from '../../base/common/observable.js';
8+
import { IObservable, IObservableWithChange, ITransaction, TransactionImpl, autorun, autorunOpts, derived, derivedOpts, derivedWithSetter, observableFromEvent, observableSignal, observableValue, observableValueOpts } from '../../base/common/observable.js';
99
import { EditorOption, FindComputedEditorOptionValueById } from '../common/config/editorOptions.js';
1010
import { LineRange } from '../common/core/lineRange.js';
1111
import { OffsetRange } from '../common/core/offsetRange.js';
@@ -155,13 +155,13 @@ export class ObservableCodeEditor extends Disposable {
155155
public readonly isReadonly = observableFromEvent(this, this.editor.onDidChangeConfiguration, () => this.editor.getOption(EditorOption.readOnly));
156156

157157
private readonly _versionId = observableValueOpts<number | null, IModelContentChangedEvent | undefined>({ owner: this, lazy: true }, this.editor.getModel()?.getVersionId() ?? null);
158-
public readonly versionId: IObservable<number | null, IModelContentChangedEvent | undefined> = this._versionId;
158+
public readonly versionId: IObservableWithChange<number | null, IModelContentChangedEvent | undefined> = this._versionId;
159159

160160
private readonly _selections = observableValueOpts<Selection[] | null, ICursorSelectionChangedEvent | undefined>(
161161
{ owner: this, equalsFn: equalsIfDefined(itemsEquals(Selection.selectionsEqual)), lazy: true },
162162
this.editor.getSelections() ?? null
163163
);
164-
public readonly selections: IObservable<Selection[] | null, ICursorSelectionChangedEvent | undefined> = this._selections;
164+
public readonly selections: IObservableWithChange<Selection[] | null, ICursorSelectionChangedEvent | undefined> = this._selections;
165165

166166

167167
public readonly positions = derivedOpts<readonly Position[] | null>(

0 commit comments

Comments
 (0)