🤖 Merge PR #65790 Jasmine - Make createSpyObj work when toString is overridden by @aj-richardson

* Make createSpyObj work when toString is overridden

If a class overrides toString (or any Object prototype method), then TS
would previously throw an error:
"Types of property 'toString' are incompatible.
Type '() => string' is not assignable to type 'string'."

* Add a test verifying object spies didn't break.
This commit is contained in:
aj-richardson
2023-06-16 08:56:43 -04:00
committed by GitHub
parent 2748f0e276
commit 37f59bd089
4 changed files with 58 additions and 2 deletions

View File

@@ -230,7 +230,11 @@ declare namespace jasmine {
};
type SpyObjMethodNames<T = undefined> = T extends undefined
? ReadonlyArray<string> | { [methodName: string]: any }
: ReadonlyArray<keyof T> | { [P in keyof T]?: T[P] extends Func ? ReturnType<T[P]> : any };
: (ReadonlyArray<keyof T> |
{ [P in keyof T]?:
// Value should be the return type (unless this is a method on Object.prototype, since all object literals contain those methods)
T[P] extends Func ? (ReturnType<T[P]> | (P extends keyof Object ? Object[P] : never)) : any
});
type SpyObjPropertyNames<T = undefined> = T extends undefined
? ReadonlyArray<string> | { [propertyName: string]: any }

View File

@@ -2072,6 +2072,30 @@ describe("better typed spys", () => {
// $ExpectType (val: string) => Spy<() => string>
spyObj.method.and.returnValue;
});
// All objects (even object literals) have Object prototype methods like toString().
// If a class overrides those methods, createSpyObj shouldn't throw any type errors.
class TestOverride {
method(): number {
return 0;
}
toString(): string {
return '';
}
toLocaleString(): string {
return '';
}
// Also make sure we don't throw type errors when using a more specific return type.
valueOf(): boolean {
return true;
}
}
it("works for classes that override Object prototype methods", () => {
jasmine.createSpyObj<TestOverride>("TestOverride", {method: 1});
});
it("allows spying on Object prototype methods", () => {
jasmine.createSpyObj<TestOverride>("TestOverride", {toString: '', toLocaleString: '', valueOf: false});
});
});
});

View File

@@ -230,7 +230,11 @@ declare namespace jasmine {
};
type SpyObjMethodNames<T = undefined> = T extends undefined
? ReadonlyArray<string> | { [methodName: string]: any }
: ReadonlyArray<keyof T> | { [P in keyof T]?: T[P] extends Func ? ReturnType<T[P]> : any };
: (ReadonlyArray<keyof T> |
{ [P in keyof T]?:
// Value should be the return type (unless this is a method on Object.prototype, since all object literals contain those methods)
T[P] extends Func ? (ReturnType<T[P]> | (P extends keyof Object ? Object[P] : never)) : any
});
type SpyObjPropertyNames<T = undefined> = T extends undefined
? ReadonlyArray<string> | { [propertyName: string]: any }

View File

@@ -2067,6 +2067,30 @@ describe("better typed spys", () => {
// $ExpectType (val: string) => Spy<() => string>
spyObj.method.and.returnValue;
});
// All objects (even object literals) have Object prototype methods like toString().
// If a class overrides those methods, createSpyObj shouldn't throw any type errors.
class TestOverride {
method(): number {
return 0;
}
toString(): string {
return '';
}
toLocaleString(): string {
return '';
}
// Also make sure we don't throw type errors when using a more specific return type.
valueOf(): boolean {
return true;
}
}
it("works for classes that override Object prototype methods", () => {
jasmine.createSpyObj<TestOverride>("TestOverride", {method: 1});
});
it("allows spying on Object prototype methods", () => {
jasmine.createSpyObj<TestOverride>("TestOverride", {toString: '', toLocaleString: '', valueOf: false});
});
});
});