Skip to content
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

New --enforceReadonly compiler option to enforce read-only semantics in type relations #58296

Open
wants to merge 26 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
f8fa8b2
Implement --strictReadonly compiler option
ahejlsberg Apr 20, 2024
453d778
Fix inconsistent 'readonly' modifiers in lib files
ahejlsberg Apr 22, 2024
0b5728a
Accept new baselines
ahejlsberg Apr 22, 2024
170bb5f
Change option name to --enforceReadonly
ahejlsberg Apr 23, 2024
97adafc
Accept new API baselines
ahejlsberg Apr 23, 2024
800c901
More baseline changes
ahejlsberg Apr 23, 2024
8c1ac64
Add tests
ahejlsberg Apr 23, 2024
88c2579
Merge branch 'main' into enforceReadonly
ahejlsberg Apr 23, 2024
d7c8851
Remove unused file
ahejlsberg Apr 23, 2024
dd6c3a8
Enforce read-only semantics in generic mapped types
ahejlsberg Apr 24, 2024
638e55c
Add more tests
ahejlsberg Apr 24, 2024
6fafe58
Compile APILibCheck.ts with --enforceReadonly
ahejlsberg Apr 24, 2024
4d16813
Accept new API baselines
ahejlsberg Apr 24, 2024
f9a132b
Fix formatting
ahejlsberg Apr 24, 2024
d616ca0
Fix comment
ahejlsberg Apr 25, 2024
474a34b
Exclude methods from strict checking
ahejlsberg Apr 25, 2024
3ee4e91
Add more tests
ahejlsberg Apr 25, 2024
5afbd2c
Remove test
ahejlsberg Apr 26, 2024
dbd7d0b
Accept new baselines
ahejlsberg Apr 29, 2024
f3bdc07
Merge branch 'main' into enforceReadonly
ahejlsberg Jul 15, 2024
90ce450
Exclude comparable relation from strict readonly checking
ahejlsberg Jul 17, 2024
c8a66e0
Add more tests
ahejlsberg Jul 17, 2024
43cc32f
Align String.raw template parameter with TemplateStringsArray
ahejlsberg Jul 17, 2024
4a668c3
Accept new baselines
ahejlsberg Jul 17, 2024
7494b23
Revert unnecessary change
ahejlsberg Jul 17, 2024
adee2e1
Accept new baselines
ahejlsberg Jul 17, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
118 changes: 74 additions & 44 deletions src/compiler/checker.ts

Large diffs are not rendered by default.

9 changes: 9 additions & 0 deletions src/compiler/commandLineParser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1022,6 +1022,15 @@ const commandOptionsWithoutBuild: CommandLineOption[] = [
description: Diagnostics.Enforces_using_indexed_accessors_for_keys_declared_using_an_indexed_type,
defaultValueDescription: false,
},
{
name: "enforceReadonly",
type: "boolean",
affectsSemanticDiagnostics: true,
affectsBuildInfo: true,
category: Diagnostics.Type_Checking,
description: Diagnostics.Ensure_that_readonly_properties_remain_read_only_in_type_relationships,
defaultValueDescription: false,
},

// Module Resolution
{
Expand Down
12 changes: 12 additions & 0 deletions src/compiler/diagnosticMessages.json
Original file line number Diff line number Diff line change
Expand Up @@ -4340,6 +4340,14 @@
"category": "Error",
"code": 4126
},
"Property '{0}' is 'readonly' in the source but not in the target.": {
"category": "Error",
"code": 4127
},
"'{0}' index signature is 'readonly' in the source but not in the target.": {
"category": "Error",
"code": 4128
},

"The current host does not support the '{0}' option.": {
"category": "Error",
Expand Down Expand Up @@ -6363,6 +6371,10 @@
"category": "Message",
"code": 6719
},
"Ensure that 'readonly' properties remain read-only in type relationships.": {
"category": "Message",
"code": 6720
},

"Default catch clause variables as 'unknown' instead of 'any'.": {
"category": "Message",
Expand Down
5 changes: 3 additions & 2 deletions src/compiler/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1710,7 +1710,7 @@ export interface AutoGenerateInfo {

/** @internal */
export interface GeneratedIdentifier extends Identifier {
readonly emitNode: EmitNode & { autoGenerate: AutoGenerateInfo; };
emitNode: EmitNode & { autoGenerate: AutoGenerateInfo; };
}

export interface QualifiedName extends Node, FlowContainer {
Expand Down Expand Up @@ -1789,7 +1789,7 @@ export interface PrivateIdentifier extends PrimaryExpression {

/** @internal */
export interface GeneratedPrivateIdentifier extends PrivateIdentifier {
readonly emitNode: EmitNode & { autoGenerate: AutoGenerateInfo; };
emitNode: EmitNode & { autoGenerate: AutoGenerateInfo; };
}

/** @internal */
Expand Down Expand Up @@ -7243,6 +7243,7 @@ export interface CompilerOptions {
downlevelIteration?: boolean;
emitBOM?: boolean;
emitDecoratorMetadata?: boolean;
enforceReadonly?: boolean;
exactOptionalPropertyTypes?: boolean;
experimentalDecorators?: boolean;
forceConsistentCasingInFileNames?: boolean;
Expand Down
14 changes: 7 additions & 7 deletions src/lib/dom.generated.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1275,8 +1275,8 @@ interface PushSubscriptionOptionsInit {
}

interface QueuingStrategy<T = any> {
highWaterMark?: number;
size?: QueuingStrategySize<T>;
readonly highWaterMark?: number;
readonly size?: QueuingStrategySize<T>;
Comment on lines +1278 to +1279
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure we can take this until the generator is updated to do this, right?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, this ultimately needs to be done in the DOM generator. However, without this workaround it's pretty much impossible to compile anything because lib.dom.d.ts fails to check properly with --enforceReadonly:

../../ts/built/local/lib.dom.d.ts(3236,11): error TS2430: Interface 'ByteLengthQueuingStrategy' incorrectly extends interface 'QueuingStrategy<ArrayBufferView>'.
  Property 'highWaterMark' is 'readonly' in the source but not in the target.
../../ts/built/local/lib.dom.d.ts(5900,11): error TS2430: Interface 'CountQueuingStrategy' incorrectly extends interface 'QueuingStrategy<any>'.
  Property 'highWaterMark' is 'readonly' in the source but not in the target.

}

interface QueuingStrategyInit {
Expand All @@ -1285,7 +1285,7 @@ interface QueuingStrategyInit {
*
* Note that the provided high water mark will not be validated ahead of time. Instead, if it is negative, NaN, or not a number, the resulting ByteLengthQueuingStrategy will cause the corresponding stream constructor to throw.
*/
highWaterMark: number;
readonly highWaterMark: number;
}

interface RTCAnswerOptions extends RTCOfferAnswerOptions {
Expand Down Expand Up @@ -6041,9 +6041,9 @@ interface DOMException extends Error {
*/
readonly code: number;
/** [MDN Reference](https://developer.mozilla.org/docs/Web/API/DOMException/message) */
readonly message: string;
message: string;
/** [MDN Reference](https://developer.mozilla.org/docs/Web/API/DOMException/name) */
readonly name: string;
name: string;
readonly INDEX_SIZE_ERR: 1;
readonly DOMSTRING_SIZE_ERR: 2;
readonly HIERARCHY_REQUEST_ERR: 3;
Expand Down Expand Up @@ -18793,7 +18793,7 @@ interface ReadableStream<R = any> {

declare var ReadableStream: {
prototype: ReadableStream;
new(underlyingSource: UnderlyingByteSource, strategy?: { highWaterMark?: number }): ReadableStream<Uint8Array>;
new(underlyingSource: UnderlyingByteSource, strategy?: { readonly highWaterMark?: number }): ReadableStream<Uint8Array>;
new<R = any>(underlyingSource: UnderlyingDefaultSource<R>, strategy?: QueuingStrategy<R>): ReadableStream<R>;
new<R = any>(underlyingSource?: UnderlyingSource<R>, strategy?: QueuingStrategy<R>): ReadableStream<R>;
};
Expand Down Expand Up @@ -19533,7 +19533,7 @@ interface SVGElementEventMap extends ElementEventMap, GlobalEventHandlersEventMa
*/
interface SVGElement extends Element, ElementCSSInlineStyle, GlobalEventHandlers, HTMLOrSVGElement {
/** @deprecated */
readonly className: any;
className: any;
readonly ownerSVGElement: SVGSVGElement | null;
readonly viewportElement: SVGElement | null;
addEventListener<K extends keyof SVGElementEventMap>(type: K, listener: (this: SVGElement, ev: SVGElementEventMap[K]) => any, options?: boolean | AddEventListenerOptions): void;
Expand Down
8 changes: 4 additions & 4 deletions src/lib/es2015.collection.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ interface Map<K, V> {
interface MapConstructor {
new (): Map<any, any>;
new <K, V>(entries?: readonly (readonly [K, V])[] | null): Map<K, V>;
readonly prototype: Map<any, any>;
prototype: Map<any, any>;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm concerned with this change as prototype is actually read-only on Map, Set, WeakMap, WeakSet, etc.:

image

}
declare var Map: MapConstructor;

Expand Down Expand Up @@ -64,7 +64,7 @@ interface WeakMap<K extends WeakKey, V> {

interface WeakMapConstructor {
new <K extends WeakKey = WeakKey, V = any>(entries?: readonly (readonly [K, V])[] | null): WeakMap<K, V>;
readonly prototype: WeakMap<WeakKey, any>;
prototype: WeakMap<WeakKey, any>;
}
declare var WeakMap: WeakMapConstructor;

Expand Down Expand Up @@ -96,7 +96,7 @@ interface Set<T> {

interface SetConstructor {
new <T = any>(values?: readonly T[] | null): Set<T>;
readonly prototype: Set<any>;
prototype: Set<any>;
}
declare var Set: SetConstructor;

Expand Down Expand Up @@ -124,6 +124,6 @@ interface WeakSet<T extends WeakKey> {

interface WeakSetConstructor {
new <T extends WeakKey = WeakKey>(values?: readonly T[] | null): WeakSet<T>;
readonly prototype: WeakSet<WeakKey>;
prototype: WeakSet<WeakKey>;
}
declare var WeakSet: WeakSetConstructor;
2 changes: 1 addition & 1 deletion src/lib/es2015.core.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -539,7 +539,7 @@ interface StringConstructor {
* @param template A well-formed template string call site representation.
* @param substitutions A set of substitution values.
*/
raw(template: { raw: readonly string[] | ArrayLike<string>; }, ...substitutions: any[]): string;
raw(template: { readonly raw: readonly string[] | ArrayLike<string>; }, ...substitutions: any[]): string;
}

interface Int8Array {
Expand Down
4 changes: 2 additions & 2 deletions src/lib/es2015.generator.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ interface GeneratorFunction {
/**
* A reference to the prototype.
*/
readonly prototype: Generator;
prototype: Generator;
}

interface GeneratorFunctionConstructor {
Expand All @@ -55,5 +55,5 @@ interface GeneratorFunctionConstructor {
/**
* A reference to the prototype.
*/
readonly prototype: GeneratorFunction;
prototype: GeneratorFunction;
}
2 changes: 1 addition & 1 deletion src/lib/es2015.promise.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ interface PromiseConstructor {
/**
* A reference to the prototype.
*/
readonly prototype: Promise<any>;
prototype: Promise<any>;

/**
* Creates a new Promise.
Expand Down
2 changes: 1 addition & 1 deletion src/lib/es2015.symbol.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ interface SymbolConstructor {
/**
* A reference to the prototype.
*/
readonly prototype: Symbol;
prototype: Symbol;

/**
* Returns a new unique Symbol value.
Expand Down
4 changes: 2 additions & 2 deletions src/lib/es2017.object.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ interface ObjectConstructor {
* Returns an array of values of the enumerable own properties of an object
* @param o Object that contains the properties and methods. This can be an object that you created or an existing Document Object Model (DOM) object.
*/
values<T>(o: { [s: string]: T; } | ArrayLike<T>): T[];
values<T>(o: { readonly [s: string]: T; } | ArrayLike<T>): T[];

/**
* Returns an array of values of the enumerable own properties of an object
Expand All @@ -15,7 +15,7 @@ interface ObjectConstructor {
* Returns an array of key/values of the enumerable own properties of an object
* @param o Object that contains the properties and methods. This can be an object that you created or an existing Document Object Model (DOM) object.
*/
entries<T>(o: { [s: string]: T; } | ArrayLike<T>): [string, T][];
entries<T>(o: { readonly [s: string]: T; } | ArrayLike<T>): [string, T][];

/**
* Returns an array of key/values of the enumerable own properties of an object
Expand Down
2 changes: 1 addition & 1 deletion src/lib/es2017.sharedmemory.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ interface SharedArrayBuffer {
}

interface SharedArrayBufferConstructor {
readonly prototype: SharedArrayBuffer;
prototype: SharedArrayBuffer;
new (byteLength: number): SharedArrayBuffer;
}
declare var SharedArrayBuffer: SharedArrayBufferConstructor;
Expand Down
4 changes: 2 additions & 2 deletions src/lib/es2018.asyncgenerator.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ interface AsyncGeneratorFunction {
/**
* A reference to the prototype.
*/
readonly prototype: AsyncGenerator;
prototype: AsyncGenerator;
}

interface AsyncGeneratorFunctionConstructor {
Expand All @@ -55,5 +55,5 @@ interface AsyncGeneratorFunctionConstructor {
/**
* A reference to the prototype.
*/
readonly prototype: AsyncGeneratorFunction;
prototype: AsyncGeneratorFunction;
}
2 changes: 1 addition & 1 deletion src/lib/es2018.intl.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ declare namespace Intl {
interface PluralRulesConstructor {
new (locales?: string | readonly string[], options?: PluralRulesOptions): PluralRules;
(locales?: string | readonly string[], options?: PluralRulesOptions): PluralRules;
supportedLocalesOf(locales: string | readonly string[], options?: { localeMatcher?: "lookup" | "best fit"; }): string[];
supportedLocalesOf(locales: string | readonly string[], options?: { readonly localeMatcher?: "lookup" | "best fit"; }): string[];
}

const PluralRules: PluralRulesConstructor;
Expand Down
6 changes: 3 additions & 3 deletions src/lib/es2020.bigint.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ interface BigInt {

interface BigIntConstructor {
(value: bigint | boolean | number | string): bigint;
readonly prototype: BigInt;
prototype: BigInt;

/**
* Interprets the low bits of a BigInt as a 2's-complement signed integer.
Expand Down Expand Up @@ -370,7 +370,7 @@ interface BigInt64Array {
}

interface BigInt64ArrayConstructor {
readonly prototype: BigInt64Array;
prototype: BigInt64Array;
new (length?: number): BigInt64Array;
new (array: Iterable<bigint>): BigInt64Array;
new (buffer: ArrayBufferLike, byteOffset?: number, length?: number): BigInt64Array;
Expand Down Expand Up @@ -642,7 +642,7 @@ interface BigUint64Array {
}

interface BigUint64ArrayConstructor {
readonly prototype: BigUint64Array;
prototype: BigUint64Array;
new (length?: number): BigUint64Array;
new (array: Iterable<bigint>): BigUint64Array;
new (buffer: ArrayBufferLike, byteOffset?: number, length?: number): BigUint64Array;
Expand Down
2 changes: 1 addition & 1 deletion src/lib/es2020.intl.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -426,7 +426,7 @@ declare namespace Intl {
*
* [MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DisplayNames/supportedLocalesOf).
*/
supportedLocalesOf(locales?: LocalesArgument, options?: { localeMatcher?: RelativeTimeFormatLocaleMatcher; }): UnicodeBCP47LocaleIdentifier[];
supportedLocalesOf(locales?: LocalesArgument, options?: { readonly localeMatcher?: RelativeTimeFormatLocaleMatcher; }): UnicodeBCP47LocaleIdentifier[];
};

interface CollatorConstructor {
Expand Down
2 changes: 1 addition & 1 deletion src/lib/es2021.promise.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ interface AggregateError extends Error {
interface AggregateErrorConstructor {
new (errors: Iterable<any>, message?: string): AggregateError;
(errors: Iterable<any>, message?: string): AggregateError;
readonly prototype: AggregateError;
prototype: AggregateError;
}

declare var AggregateError: AggregateErrorConstructor;
Expand Down
4 changes: 2 additions & 2 deletions src/lib/es2021.weakref.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ interface WeakRef<T extends WeakKey> {
}

interface WeakRefConstructor {
readonly prototype: WeakRef<any>;
prototype: WeakRef<any>;

/**
* Creates a WeakRef instance for the given target value.
Expand Down Expand Up @@ -46,7 +46,7 @@ interface FinalizationRegistry<T> {
}

interface FinalizationRegistryConstructor {
readonly prototype: FinalizationRegistry<any>;
prototype: FinalizationRegistry<any>;

/**
* Creates a finalization registry with an associated cleanup callback
Expand Down
Loading