[6.x] [core/public/deepFreeze] fix recursive type for better array support (#22904) (#22932)

Backports the following commits to 6.x:
 - [core/public/deepFreeze] fix recursive type for better array support  (#22904)
This commit is contained in:
Spencer 2018-09-11 10:27:32 -07:00 committed by GitHub
parent 526c48f2cd
commit cfd2a8fee8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 49 additions and 41 deletions

View file

@ -17,17 +17,34 @@
* under the License.
*/
import { deepFreeze } from '../deep_freeze';
import { deepFreeze } from '../../deep_freeze';
const obj = deepFreeze({
foo: {
bar: {
baz: 1,
deepFreeze(
{
foo: {
bar: {
baz: 1,
},
},
},
});
}
).foo.bar.baz = 2;
delete obj.foo;
obj.foo = 1;
obj.foo.bar.baz = 2;
obj.foo.bar.box = false;
deepFreeze(
{
foo: [
{
bar: 1,
},
],
}
).foo[0].bar = 2;
deepFreeze(
{
foo: [1],
}
).foo[0] = 2;
deepFreeze({
foo: [1],
}).foo.push(2);

View file

@ -6,8 +6,7 @@
"esnext"
]
},
"include": [
"frozen_object_mutation.ts",
"../deep_freeze.ts"
"files": [
"index.ts"
]
}

View file

@ -75,28 +75,17 @@ it('prevents reassigning items in a frozen array', () => {
});
it('types return values to prevent mutations in typescript', async () => {
const result = await execa.stdout(
'tsc',
[
'--noEmit',
'--project',
resolve(__dirname, '__fixtures__/frozen_object_mutation.tsconfig.json'),
],
{
cwd: resolve(__dirname, '__fixtures__'),
reject: false,
}
);
await expect(
execa.stdout('tsc', ['--noEmit'], {
cwd: resolve(__dirname, '__fixtures__/frozen_object_mutation'),
})
).rejects.toThrowErrorMatchingInlineSnapshot(`
"Command failed: tsc --noEmit
const errorCodeRe = /\serror\s(TS\d{4}):/g;
const errorCodes = [];
while (true) {
const match = errorCodeRe.exec(result);
if (!match) {
break;
}
errorCodes.push(match[1]);
}
expect(errorCodes).toEqual(['TS2704', 'TS2540', 'TS2540', 'TS2339']);
index.ts(30,11): error TS2540: Cannot assign to 'baz' because it is a constant or a read-only property.
index.ts(40,10): error TS2540: Cannot assign to 'bar' because it is a constant or a read-only property.
index.ts(42,1): error TS2542: Index signature in type 'RecursiveReadonlyArray<number>' only permits reading.
index.ts(50,8): error TS2339: Property 'push' does not exist on type 'RecursiveReadonlyArray<number>'.
"
`);
});

View file

@ -19,9 +19,12 @@
type Freezable = { [k: string]: any } | any[];
type RecursiveReadOnly<T> = T extends Freezable
? Readonly<{ [K in keyof T]: RecursiveReadOnly<T[K]> }>
: T;
// if we define this inside RecursiveReadonly TypeScript complains
interface RecursiveReadonlyArray<T> extends ReadonlyArray<RecursiveReadonly<T>> {}
type RecursiveReadonly<T> = T extends any[]
? RecursiveReadonlyArray<T[number]>
: T extends object ? Readonly<{ [K in keyof T]: RecursiveReadonly<T[K]> }> : T;
export function deepFreeze<T extends Freezable>(object: T) {
// for any properties that reference an object, makes sure that object is
@ -32,5 +35,5 @@ export function deepFreeze<T extends Freezable>(object: T) {
}
}
return Object.freeze(object) as RecursiveReadOnly<T>;
return Object.freeze(object) as RecursiveReadonly<T>;
}