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

Assignability should check an index signature or rest type of the source against optional properties of the target. #27591

Closed
wants to merge 11 commits into from

Conversation

mattmccutchen
Copy link
Contributor

Fix one good break in the compiler itself.

Fixes #27144.

against optional properties of the target.

Fix one good break in the compiler itself.

Fixes microsoft#27144.
@RyanCavanaugh
Copy link
Member

@typescript-bot test this

@typescript-bot
Copy link
Collaborator

typescript-bot commented Oct 9, 2018

Heya @RyanCavanaugh, I've started to run the extended test suite on this PR at 3b5caa3. You can monitor the build here. It should now contribute to this PR's status checks.

@RyanCavanaugh
Copy link
Member

@typescript-bot test this

@typescript-bot
Copy link
Collaborator

typescript-bot commented Jan 24, 2019

Heya @RyanCavanaugh, I've started to run the extended test suite on this PR at 3b5caa3. You can monitor the build here. It should now contribute to this PR's status checks.

@RyanCavanaugh
Copy link
Member

This turned up some very strange changes in the RWC baseline that need reduction so that we can understand why they're happening and whether or not they should

src/compiler/checker.ts Outdated Show resolved Hide resolved
src/compiler/checker.ts Outdated Show resolved Hide resolved
@sandersn
Copy link
Member

Here's a problem found in firebase-firestore:

interface Opt {
    a?: string
    b?: number
}
interface Dunk {
    [s: string]: {}
}
function f(o: Opt, d: Dunk) {
    d = o
}

Opt should be assignable to Dunk, but with the current PR, it's not.

@sandersn
Copy link
Member

In the ancient snapshot of VS Code in the RWC, a cast is no longer allowed when the target has protected properties, even when there is some shared property. Here's a smaller repro:

interface UView {
	size: number;
	sizing: unknown;
	fixedSize: number;
	minimumSize: number;
	maximumSize: number;
	render(container: unknown, orientation: unknown): void;
	layout(size: number, orientation: unknown): void;
	focus(): void;
        extraExtraExtra: string;
}
abstract class Vue {

	size: number;
	protected _sizing: unknown;
	protected _fixedSize: number;
	protected _minimumSize: number;

	get sizing(): unknown { return this._sizing; }
	get fixedSize(): number { return this._fixedSize; }
	get minimumSize(): number { return this._minimumSize; }
	get maximumSize(): number { return Number.POSITIVE_INFINITY; }

	protected setFlexible(size?: number): void {
	}

	protected setFixed(size?: number): void {
	}

	abstract render(container: unknown, orientation: unknown): void;
	abstract focus(): void;
	abstract layout(size: number, orientation: unknown): void;
}
function ohno() {
    let uv: UView;
    let i2: Vue = <Vue>uv; // error here
}

And here's the smallest repro I could get:

interface Source {
    shared: unknown;

    extraExtraExtra: string;
}
abstract class Target {
    get shared(): unknown { return {}; }

    protected _secret: unknown;
}
function ohno() {
    let uv: Source;
    let i2: Target = <Target>uv; // error here
}

Copy link
Member

@sandersn sandersn left a comment

Choose a reason for hiding this comment

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

Need to address repros found from RWC failures.

@mattmccutchen
Copy link
Contributor Author

Here's a problem found in firebase-firestore: [...]

Opt should be assignable to Dunk, but with the current PR, it's not.

That has nothing to do with this PR: I get the same error with TypeScript 3.3.3, which is expected because the target has an index signature and the source doesn't. If you meant "Dunk should be assignable to Opt, but with the current PR, it's not", then yes, that is precisely the change being made in the current PR, and we'd need to see more context to evaluate whether it's reasonable to require that code to be changed to satisfy the stricter checking in this PR.

In the ancient snapshot of VS Code in the RWC, a cast is no longer allowed when the target has protected properties, even when there is some shared property.

With both examples, I get the same error with TypeScript 3.3.3 as with this PR, which is expected: a cast requires that one type be comparable to the other.

@RyanCavanaugh
Copy link
Member

@sandersn Please update this and re-run RWC so we can discuss in a week or two at a design meeting

@sandersn sandersn added the For Backlog Bug PRs that fix a backlog bug label Feb 1, 2020
@sandersn
Copy link
Member

sandersn commented Mar 5, 2020

@ahejlsberg this might be related to your work on assignability of optionals with respect to intersections.

@sandersn
Copy link
Member

sandersn commented Mar 6, 2020

@typescript-bot user test this
@typescript-bot test this

@typescript-bot
Copy link
Collaborator

typescript-bot commented Mar 6, 2020

Heya @sandersn, I've started to run the parallelized community code test suite on this PR at 4534335. You can monitor the build here.

@typescript-bot
Copy link
Collaborator

typescript-bot commented Mar 6, 2020

Heya @sandersn, I've started to run the extended test suite on this PR at 4534335. You can monitor the build here.

@typescript-bot
Copy link
Collaborator

The user suite test run you requested has finished and failed. I've opened a PR with the baseline diff from master.

@sandersn
Copy link
Member

sandersn commented Mar 6, 2020

Failures look about the same in RWC, although I've not had a chance to check in detail. The user/docker tests have a lot more and more up-to-date code than when this PR was opened, so I'll look at that first. Plus the diff is a public PR.

There are no breaks from tuple rest types, but this isn't surprising since those are quite rare.

  • azure-sdk has two breaks, 1 unique.
  • office-ui-fabric has 34 failures, but only 5 unique.
  • vscode has 4 breaks, 2 unique. Both in electron-browser.

Iniitially, I guess that the breaks are correct. I'll have to actually look at the code, which means cloning and setting it up on my laptop...

@weswigham
Copy link
Member

@typescript-bot pack this

@typescript-bot
Copy link
Collaborator

typescript-bot commented Mar 6, 2020

Heya @weswigham, I've started to run the tarball bundle task on this PR at 4534335. You can monitor the build here.

@weswigham
Copy link
Member

The test failures in this PR need to be addressed before a tarball can be produced~

@sandersn
Copy link
Member

sandersn commented Mar 6, 2020

Looking closer at VSCode, there's an example like this:

export interface ITelemetryData {
    from?: string;
    target?: string;
    [key: string]: any;
}
export type Tags = { [index: string]: boolean | number | string | undefined };
let d: ITelemetryData = tags

This has the incorrect error that

[XX:XX:XX] Error: /vscode/src/vs/workbench/contrib/tags/electron-browser/workspaceTags.ts(215,52): Argument of type 'Tags' is not assignable to parameter of type 'ITelemetryData'.
  Index signature is incompatible with property 'from'.
    Type 'string | number | boolean | undefined' is not assignable to type 'string | undefined'.

Which is true, but the assignment is still fine because ITelemetryData has an index signature to any. So I think the checks should not apply when the target has an index signature.

@sandersn
Copy link
Member

sandersn commented Mar 6, 2020

The other VSCode error is not catching a bug either, although it's closer:

(['includeSystemInfo', 'includeProcessInfo', 'includeWorkspaceInfo', 'includeExtensions', 'includeSearchedExtensions', 'includeSettingsSearchDetails'] as const).forEach(elementId => {
                var x = { [elementId]: !this.issueReporterModel.getData()[elementId] } as unknown as Partial<IssueReporterData>
                let pird: Partial<IssueReporterData> = x

where

export interface IssueReporterData {
	issueType: IssueType;
	issueDescription?: string;

	versionInfo?: any;
	systemInfo?: SystemInfo;
	processInfo?: any;
	workspaceInfo?: any;

	includeSystemInfo: boolean;
	includeWorkspaceInfo: boolean;
	includeProcessInfo: boolean;
	includeExtensions: boolean;
	includeSearchedExtensions: boolean;
	includeSettingsSearchDetails: boolean;

	numberOfThemeExtesions?: number;
	allExtensions: IssueReporterExtensionData[];
	enabledNonThemeExtesions?: IssueReporterExtensionData[];
	extensionsDisabled?: boolean;
	fileOnExtension?: boolean;
	selectedExtension?: IssueReporterExtensionData;
	actualSearchResults?: ISettingSearchResult[];
	query?: string;
	filterResultCount?: number;
}

In this case, the intent is sound, but we give x: { [s: string]: boolean } instead of x: { includeSystemInfo?: boolean, includeWorkspaceInfo?: boolean, ... }. Then that index signature is not assignment to IssueReporterData.

@sandersn
Copy link
Member

sandersn commented Mar 6, 2020

I pushed a commit to not handle private identifiers. That might not be the right thing, since I think private identifiers can be optional, but I'm just trying to get the tests to pass for now.

@DanielRosenwasser
Copy link
Member

That might not be the right thing, since I think private identifiers can be optional,

An index signature simply doesn't need to apply to a private field because there's no way to dynamically look it up through an element access of the form expr[expr].

@DanielRosenwasser
Copy link
Member

(though it's not totally off limits in future ECMAScript)

@sandersn
Copy link
Member

An API test breaks, finding that CompilerOptionsValue is not assignable to WatchFileKind:

    type CompilerOptions = number | ...
    export enum WatchFileKind {
        FixedPollingInterval = 0,
        PriorityPollingInterval = 1,
        DynamicPriorityPolling = 2,
        UseFsEvents = 3,
        UseFsEventsOnParentDirectory = 4
    }

This is a very subtle bug because it is buried so deep, and it goes through a parameter in a signature so the variance flips, making it extra confusing. But it is correct.
I just skipped it with a cast to any for now; if this sample is trying to show 'correct' usage of our API, it's showing that API is error-prone!

@sandersn
Copy link
Member

The azure-sdk error is probably good, easy to understand, and in fact got fixed 4 days ago in 87e59fd300. It seems like the predicate used to distinguish X | { [s: string]: X } was insufficient, so one code path was incorrectly passing a { [s: string]: X } to a function that expects X.

(It's possible that the predicate was fine, and the compiler just couldn't prove it.)

@sandersn
Copy link
Member

Welp, now the LKG build catches a failures in the server/editorServices.ts, in which WatchOptions is not assignable to ExternalProjectCompilerOptions. It's a correct error: WatchOptions has an index signature that includes a whole bunch of types, but ExternalProjectCompilerOptions has many optional properties of type boolean | undefined.

This is sound but I've been staring at this too long; I can't tell if it's desirable or not.

@@ -36,8 +36,8 @@ function watchMain() {
readDirectory: ts.sys.readDirectory,
realpath: ts.sys.realpath,

watchFile: ts.sys.watchFile!,
watchDirectory: ts.sys.watchDirectory!,
watchFile: ts.sys.watchFile! as any,
Copy link
Member

Choose a reason for hiding this comment

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

what?

Copy link
Member

Choose a reason for hiding this comment

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

ts.sys.watchFile is a function with a final parameter of type WatchOptions, which has a property of type WatchFileKind, a numeric enum. CompilerHost's watchFile, the target signature, has a final parameter of type CompilerOptions, which does not have a matching property, but does have an index signature that includes number. But number isn't assignable to an enum.

This is technically sound, because people could call watchFile with an options bug containing any number at all (or a lot of other types), but ts.sys.watchFile can only handle WatchFileKind on certain of its properties.

However, I don't know how likely this error is to happen in practise. We haven't had trouble as far as we know.

Copy link
Member

@sandersn sandersn left a comment

Choose a reason for hiding this comment

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

From my observation, this change is:

  1. Technically correct in most (probably all?) cases.
  2. But not practically useful in the majority of cases.
  3. Hard to recover from in many cases.

I vote not to take this change unless I see something that changes my mind. For example:

  1. Data that show a majority of good, easy-to-understand bugs being caught.
  2. An easy explanation of how to fix the errors mechanically.

@DanielRosenwasser I'd like a second opinion if you have time.

@sandersn
Copy link
Member

@typescript-bot user test this

@typescript-bot
Copy link
Collaborator

typescript-bot commented Mar 13, 2020

Heya @sandersn, I've started to run the parallelized community code test suite on this PR at 133968c. You can monitor the build here.

@typescript-bot
Copy link
Collaborator

The user suite test run you requested has finished and failed. I've opened a PR with the baseline diff from master.

@weswigham
Copy link
Member

@typescript-bot pack this now that it's fixed up

@typescript-bot
Copy link
Collaborator

typescript-bot commented Mar 13, 2020

Heya @weswigham, I've started to run the tarball bundle task on this PR at 133968c. You can monitor the build here.

@sandersn
Copy link
Member

sandersn commented Apr 1, 2020

This PR hasn't seen any activity for quite a while, so I'm going to close it to keep the number of open PRs manageable. Feel free to open a fresh PR or continue the discussion here.

@sandersn sandersn closed this Apr 1, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
For Backlog Bug PRs that fix a backlog bug
Projects
Archived in project
Development

Successfully merging this pull request may close these issues.

Index signature is assignable to weak type whose properties don't match the signature type
7 participants