-
Notifications
You must be signed in to change notification settings - Fork 12.5k
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
Conversation
against optional properties of the target. Fix one good break in the compiler itself. Fixes microsoft#27144.
@typescript-bot test this |
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. |
@typescript-bot test this |
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. |
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 |
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. |
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
} |
There was a problem hiding this 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.
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.
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. |
@sandersn Please update this and re-run RWC so we can discuss in a week or two at a design meeting |
@ahejlsberg this might be related to your work on assignability of optionals with respect to intersections. |
@typescript-bot user test this |
The user suite test run you requested has finished and failed. I've opened a PR with the baseline diff from master. |
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.
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... |
@typescript-bot pack this |
Heya @weswigham, I've started to run the tarball bundle task on this PR at 4534335. You can monitor the build here. |
The test failures in this PR need to be addressed before a tarball can be produced~ |
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
Which is true, but the assignment is still fine because |
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 |
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. |
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 |
(though it's not totally off limits in future ECMAScript) |
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. |
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 (It's possible that the predicate was fine, and the compiler just couldn't prove it.) |
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: 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, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what?
There was a problem hiding this comment.
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.
There was a problem hiding this 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:
- Technically correct in most (probably all?) cases.
- But not practically useful in the majority of cases.
- Hard to recover from in many cases.
I vote not to take this change unless I see something that changes my mind. For example:
- Data that show a majority of good, easy-to-understand bugs being caught.
- An easy explanation of how to fix the errors mechanically.
@DanielRosenwasser I'd like a second opinion if you have time.
@typescript-bot user test this |
The user suite test run you requested has finished and failed. I've opened a PR with the baseline diff from master. |
@typescript-bot pack this now that it's fixed up |
Heya @weswigham, I've started to run the tarball bundle task on this PR at 133968c. You can monitor the build here. |
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. |
Fix one good break in the compiler itself.
Fixes #27144.