GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — Strict function types by ahejlsberg · Pull Request #18654 · microsoft/TypeScript

Conversation

@ahejlsberg

@ahejlsberg Anders Hejlsberg (ahejlsberg) commented Sep 21, 2017 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Member

With this PR we introduce a --strictFunctionTypes mode in which function type parameter positions are checked contravariantly instead of bivariantly. The stricter checking applies to all function types, except those originating in method or construcor declarations. Methods are excluded specifically to ensure generic classes and interfaces (such as Array<T>) continue to mostly relate covariantly. The impact of strictly checking methods would be a much bigger breaking change as a large number of generic types would become invariant (even so, we may continue to explore this stricter mode).

The --strictFunctionTypes switch is part of the --strict family of switches, meaning that it defaults to on in --strict mode. This PR is therefore a breaking change only in --strict mode.

Consider the following example in which Animal is the supertype of Dog and Cat:

declare let f1: (x: Animal) => void;
declare let f2: (x: Dog) => void;
declare let f3: (x: Cat) => void;
f1 = f2;  // Error with --strictFunctionTypes
f2 = f1;  // Ok
f2 = f3;  // Error

The first assignment is permitted in default type checking mode, but flagged as an error in strict function types mode. Intuitively, the default mode permits the assignment because it is possibly sound, whereas strict function types mode makes it an error because it isn't provably sound. In either mode the third assignment is an error because it is never sound.

Another way to describe the example is that the type (x: T) => void is bivariant (i.e. covariant or contravariant) for T in default type checking mode, but contravariant for T in strict function types mode.

Another example:

interface Comparer<T> {
    compare(a: T, b: T): number;
}

declare let animalComparer: Comparer<Animal>;
declare let dogComparer: Comparer<Dog>;

animalComparer = dogComparer;  // Ok because of bivariance
dogComparer = animalComparer;  // Ok

In --strictFunctionTypes mode the first assignment is still permitted because compare is declared as a method. Effectively, T is bivariant in Comparer<T> because it is used only in method parameter positions. However, changing compare to be a property with a function type causes stricter checking to take effect:

interface Comparer<T> {
    compare: (a: T, b: T) => number;
}

declare let animalComparer: Comparer<Animal>;
declare let dogComparer: Comparer<Dog>;

animalComparer = dogComparer;  // Error
dogComparer = animalComparer;  // Ok

The first assignment is now an error. Effectively, T is contravariant in Comparer<T> because it is used only in function type parameter positions.

By the way, note that whereas some languages (e.g. C# and Scala) require variance annotations (out/in or +/-), variance emerges naturally from the actual use of a type parameter within a generic type due to TypeScript's structural type system.

We also improve type inference involving contravariant positions in this PR:

function combine<T>(...funcs: ((x: T) => void)[]): (x: T) => void {
    return x => {
        for (const f of funcs) f(x);
    }
}

function animalFunc(x: Animal) {}
function dogFunc(x: Dog) {}

let combined = combine(animalFunc, dogFunc);  // (x: Dog) => void

Above, all inferences for T originate in contravariant positions, and we therefore infer the best common subtype for T. This contrasts with inferences from covariant positions, where we infer the best common supertype. Note that inferences from covariant positions have precedence over inferences from contravariant positions.

Fixes #6102.
Fixes #7472.
Fixes #9514.
Fixes #9765.
Fixes #12498.
Fixes #13248.
Fixes #15579.
Fixes #18337.
Fixes #18466.

Also related are #10717 and #14973.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

aluanhaddad, Brooooooklyn, trotyl, HerringtonDarkholme, FabianLauer, plantain-00, dmikis, MrKomish, federicoce-moravia, sledorze, and 34 more reacted with thumbs up emoji RyanCavanaugh, aluanhaddad, trotyl, HerringtonDarkholme, plantain-00, federicoce-moravia, sebald, brendanzab, sean-vieira, hccampos, and 18 more reacted with hooray emoji Strate reacted with confused emoji aluanhaddad, alexrock, trotyl, HerringtonDarkholme, plantain-00, federicoce-moravia, sledorze, sean-vieira, hccampos, leoasis, and 10 more reacted with heart emoji
All reactions
  • 👍 44 reactions
  • 🎉 28 reactions
  • 😕 1 reaction
  • ❤️ 20 reactions
Anders Hejlsberg (ahejlsberg) added 12 commits September 18, 2017 06:33
@ahejlsberg
Introduce --strictFunctionTypes mode
12f5dd8
@ahejlsberg
Use dedicated marker types for variance determination
f8ff7f7
@ahejlsberg
Add quick path for computing array variance as it is already known
670d711
@ahejlsberg
Handle contravariance in type inference
a0fa69f
@ahejlsberg
Add comments
b58e0fb
@ahejlsberg
Handle special case of 'void' type arguments for covariant type param…
84f7afd
…eters
@ahejlsberg
Accept new baselines
54eadef
@ahejlsberg
Use methods in dom.generated.d.ts to opt out of strict checks
44cc8c5
@ahejlsberg
Update tsconfig baselines
dd466ae
@ahejlsberg
Revert dom.generated.d.ts and fix duplicate declarations
24698dd
@ahejlsberg
Properly flag and structurally compare marker type references
f8e2cc1
@ahejlsberg
Update comment
589e1f4
@msftclas Microsoft Contribution License Agreements (msftclas) added the cla-not-required label Sep 21, 2017
@ahejlsberg
Anders Hejlsberg (ahejlsberg) requested review from Daniel Rosenwasser (DanielRosenwasser) and Mohamed Hegazy (mhegazy) September 21, 2017 17:00
@ahejlsberg Anders Hejlsberg (ahejlsberg) added this to the TypeScript 2.6 milestone Sep 21, 2017
@ahejlsberg
Anders Hejlsberg (ahejlsberg) requested review from Ryan Cavanaugh (RyanCavanaugh) and Nathan Shively-Sanders (sandersn) September 21, 2017 17:08
@sandersn

Nathan Shively-Sanders (sandersn) commented Sep 21, 2017

Copy link
Copy Markdown
Member

I ran RWC on this and saw changes only in elaborations and order of printing union types. No errors were added or removed, and no types changed.

The changes appear to result from better type caching leading to less elaboration. The only interesting result was from the Azure Framework tests, where one incorrectly duplicated error disappeared entirely, maybe because the whole error was already issued just prior.

I'll run DefinitelyTyped next and look for diffs.

aluanhaddad reacted with thumbs up emoji aluanhaddad reacted with hooray emoji
All reactions
  • 👍 1 reaction
  • 🎉 1 reaction

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@ahejlsberg

Anders Hejlsberg (ahejlsberg) commented Sep 21, 2017

Copy link
Copy Markdown
Member Author

Nathan Shively-Sanders (@sandersn) When running DefinitelyTyped, be sure to run both with and without --strict. I'm particularly interested in the results with --strict.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@sandersn

Nathan Shively-Sanders (sandersn) commented Sep 21, 2017 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Member

Ah, good point. I forgot --strict the first time around. First I compiled everything using 0ac8406, the commit before this PR. Then I compiled again with this PR's commits and diffed the changes. Interestingly, a few packages on DefinitelyTyped still have more errors even without --strictFunctionTypes. The errors look like they come from tighter variance checking too, so it's likely that some changes are escaping from behind the flag.

Take a look at the test files for recompose, rx-lite and rx.wamp (which depends on rx-lite). Recompose is clearer to see (the rx* errors are obscured by overloads in rx-lite):

recompose-tests.tsx(248,7): error TS2345: Argument of type 'InferableComponentEnhancerWithProps<{ count: number; } & { update: (state: number) => number; }, {}>' is not assignable to parameter of type 'ComponentEnhancer<any, any> | InferableComponentEnhancerWithProps<{}, {}>'.
  Type 'InferableComponentEnhancerWithProps<{ count: number; } & { update: (state: number) => number; }, {}>' is not assignable to type 'InferableComponentEnhancerWithProps<{}, {}>'.
    Type '{}' is not assignable to type '{ count: number; } & { update: (state: number) => number; }'.
      Type '{}' is not assignable to type '{ count: number; }'.
        Property 'count' is missing in type '{}'.

Notice that the direction of assignability switches, I think because of contravariance.

In total, I noticed changes with

  • recompose
  • rx-lite
  • rx.wamp (for nearly the same reason as rx-lite)
  • jasmine
  • a few other projects had different line numbers for errors in lib.d.ts, which doesn't make sense to me.

I'll take a look at the results with --strictFunctionChecks turned on next. It's only halfway done, but there are already lots of failures, so I'll only provide a summary.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

Anders Hejlsberg (ahejlsberg) added 2 commits September 21, 2017 21:31
@ahejlsberg
Always perform structural comparison when variance check fails
afc8a26
@ahejlsberg
Add tests
70e8f73
@ahejlsberg

Anders Hejlsberg (ahejlsberg) commented Sep 22, 2017

Copy link
Copy Markdown
Member Author

Nathan Shively-Sanders (@sandersn) With the latest commits we revert to always performing a structural comparison when the relationships indicated by getVariances don't hold. In effect, the variance information computed by getVariances is now an indication of which relationships to try first as an optimization, but it doesn't preclude other relationships. In the default checking mode we now behave exactly the same way as before, so I would expect no changes to RWC baselines or DefinitelyTyped. I would expect to see new errors only in --strictFunctionTypes mode.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

RyanCavanaugh
Ryan Cavanaugh (RyanCavanaugh) reviewed Sep 22, 2017
View reviewed changes
Comment thread src/compiler/checker.ts
}
if (type.flags & TypeFlags.TypeParameter || objectFlags & ObjectFlags.ClassOrInterface) {
const name = symbolToName(type.symbol, context, SymbolFlags.Type, /*expectsIdentifier*/ false);
const name = type.symbol ? symbolToName(type.symbol, context, SymbolFlags.Type, /*expectsIdentifier*/ false) : createIdentifier("?");

@RyanCavanaugh Ryan Cavanaugh (RyanCavanaugh) Sep 22, 2017

Copy link
Copy Markdown
Member

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.

When does this case occur?

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@ahejlsberg Anders Hejlsberg (ahejlsberg) Sep 26, 2017

Copy link
Copy Markdown
Member Author

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.

This happens when we have a synthetic type parameter, such as the type parameters we create for tuple types and the markerXXXType type parameters we use for variance determination.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
@RyanCavanaugh

Ryan Cavanaugh (RyanCavanaugh) commented Sep 22, 2017

Copy link
Copy Markdown
Member

LGTM but I'd like to see what happens if we run RWC with this forced on

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@sandersn

Nathan Shively-Sanders (sandersn) commented Sep 22, 2017

Copy link
Copy Markdown
Member

With the latest commits, both RWC and DT crash much more due to running out of memory. I only get about 2/3 of the RWC tests to succeed, and DT crashes at 'bookshelf'.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@ahejlsberg

Anders Hejlsberg (ahejlsberg) commented Sep 22, 2017

Copy link
Copy Markdown
Member Author

Nathan Shively-Sanders (@sandersn) Is that with or without --strictFunctionTypes?

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@sandersn

Nathan Shively-Sanders (sandersn) commented Sep 22, 2017

Copy link
Copy Markdown
Member

With --strictFunctionTypes

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@ahejlsberg

Anders Hejlsberg (ahejlsberg) commented Sep 22, 2017

Copy link
Copy Markdown
Member Author

Nathan Shively-Sanders (@sandersn) It is likely because we do more structural comparisons of types. There are enough edge cases (e.g. any or void type arguments) where types don't act exactly according to the gathered variance information, so if the optimized first check of the type arguments doesn't succeed we need to do a structural comparison. We'll have to look at the specific cases, but it's not clear what else we could do.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@ahejlsberg
Add error elaboration test
c2344e0
@ahejlsberg

Anders Hejlsberg (ahejlsberg) commented Oct 2, 2017

Copy link
Copy Markdown
Member Author

Latest commits address code review feedback. Also, we improve error elaboration for generic types where one or more type parameters are invariant. In some cases generic types that are covariant in regular type checking mode become invariant in --strictFunctionTypes mode because one or more type parameters are used in both co- and contravariant positions with the generic type. In order to make it easier to diagnose why such types are invariant, if any of the type parameters are invariant we reset the reported errors and instead force a structural comparison (which will include elaborations that reveal the reason).

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@ahejlsberg
Anders Hejlsberg (ahejlsberg) merged commit 884c72e into master Oct 2, 2017
@ahejlsberg
Anders Hejlsberg (ahejlsberg) deleted the strictFunctionTypes branch October 2, 2017 22:09
@DanielRosenwasser Daniel Rosenwasser (DanielRosenwasser) mentioned this pull request Oct 2, 2017
Design Meeting Notes, 10/2/2017 #18895
Closed
@sandersn Nathan Shively-Sanders (sandersn) mentioned this pull request Oct 2, 2017
Strict function variance fixes DefinitelyTyped/DefinitelyTyped#20219
Merged
@basarat Basarat Ali Syed (basarat) mentioned this pull request Oct 3, 2017
Function variance changes basarat/typescript-book#331
Open
@mhegazy Mohamed Hegazy (mhegazy) mentioned this pull request Oct 3, 2017
FATAL ERROR: CALL_AND_RETRY_LAST Allocation failed - JavaScript heap out of memory #18411
Closed
@ghost ghost mentioned this pull request Oct 4, 2017
(regressions) Property 'something' does not exist on type '{}' and Types of parameters 'field' and 'value' are incompatible #18941
Closed
@ahejlsberg Anders Hejlsberg (ahejlsberg) mentioned this pull request Oct 7, 2017
strictFunctionTypes option should strictly check callback parameters of override methods #19007
Closed
@goodmind

andretshurotshka (goodmind) commented Oct 8, 2017

Copy link
Copy Markdown

Anders Hejlsberg (@ahejlsberg) do you have feedback on my latest example?

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@xnnkmd

xnnkmd commented Oct 10, 2017

Copy link
Copy Markdown

Anders Hejlsberg (@ahejlsberg) Even though you mention only 3% of type packages are affected, I think the real negative impact is in much higher considering that major packages like angular, express, hapi, cucumber, selenium-webdriver etc. are affected. Hopefully the undocummented "skipLibCheck" option can rescue some projects.

styfle and zpdDG4gta8XKpMCd reacted with confused emoji
All reactions
  • 😕 2 reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@gcanti Giulio Canti (gcanti) mentioned this pull request Oct 11, 2017
HKT abstraction no more typecheck with latest typescript dev version. gcanti/fp-ts#244
Closed
@ahejlsberg

Anders Hejlsberg (ahejlsberg) commented Oct 12, 2017

Copy link
Copy Markdown
Member Author

xnnkmd The list above doesn't show it, but in practically all cases (including all of the ones you mention) the errors are in the tests associated with the type package, not in the package itself. So, there should be no need to use --skipLibCheck.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@styfle

Steven (styfle) commented Oct 12, 2017

Copy link
Copy Markdown
Contributor

the errors are in the tests associated with the type package, not in the package itself

Anders Hejlsberg (@ahejlsberg) Can you post a list of packages where only the package itself is affected? (not the tests)

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@zpdDG4gta8XKpMCd

ZpdDG4gta (zpdDG4gta8XKpMCd) commented Oct 12, 2017

Copy link
Copy Markdown

gentlemen, pardon me, but i don't understand your attitude towards these mighty new features... even if we break 97% of all code out there, even then:

  • it's under the flag --strictFunctionTypes, so if you are scared of it, stay away from it! so if you did stay away from it, you are in the old safe TypeScript and nothing brakes

  • secondly, the code that breaks is ill and it's a good thing that you see it breaks, it means you know where you weak parts are, so please be grateful, will you?

gcnew, nicolashenry, adidahiya, SlurpTheo, and jesseschalken reacted with thumbs up emoji gcnew reacted with laugh emoji
All reactions
  • 👍 5 reactions
  • 😄 1 reaction

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@sandersn

Nathan Shively-Sanders (sandersn) commented Oct 12, 2017 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Member

DefinitelyTyped/DefinitelyTyped#20219 fixes the packages on DefinitelyTyped that break with --strictFunctionTypes. Plus it updates the tests for many "popular" packages whose tests will break so you can take a look to see how you might have to update your code.

Packages fixed by DefinitelyTyped/DefinitelyTyped#20219:

  • backbone
  • bootbox
  • cordova-plugin-appbrowser
  • ibm-mobilefirst
  • jointjs
  • jquery
  • knockout
  • lodash
  • ramda
  • react

I also fixed tests in

  • node
  • log4js
  • q
  • react-native
  • react-redux
  • sharepoint
  • webpack

Notes:

  • Here, "popular" just means a random selection of packages I have heard of. I didn't fix hapi's tests, for example, even though I think it's fairly popular.
  • I didn't submit PRs to projects that bundles types with their packages. I don't have a good way to scan all Typescript that eventually ends up on npm.
styfle, xnnkmd, SlurpTheo, and StefanRein reacted with hooray emoji
All reactions
  • 🎉 4 reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@SlurpTheo SlurpTheo mentioned this pull request Oct 17, 2017
'Discarding Parameters' plus 'Duck Typing' makes every parameter optional? #9352
Closed
@dgwaldo Don W. (dgwaldo) mentioned this pull request Oct 17, 2017
#6930: Updates Dojox Charting definition module exports DefinitelyTyped/DefinitelyTyped#20654
Merged
@mmc41 Morten Christensen (mmc41) mentioned this pull request Oct 19, 2017
@types/hapi Update to 17.0 DefinitelyTyped/DefinitelyTyped#20670
Closed
2 of 4 tasks
@ahejlsberg Anders Hejlsberg (ahejlsberg) mentioned this pull request Oct 24, 2017
strict function argument types #19432
Closed
This was referenced Nov 6, 2017
Strange error with --strictFunctionTypes #19746
Closed
Wrong assigment is not flagged by compiler #19820
Closed
Union type method parameters in base class accepts incompatible types in subclass overrides #19832
Closed
@ahejlsberg Anders Hejlsberg (ahejlsberg) mentioned this pull request Dec 17, 2017
Inconsistent compatibility of generic interfaces #20738
Closed
@ahejlsberg Anders Hejlsberg (ahejlsberg) mentioned this pull request Jan 3, 2018
Union types confound interface implementation checks #20983
Closed
@DanielRosenwasser Daniel Rosenwasser (DanielRosenwasser) mentioned this pull request Feb 24, 2018
Overridden method parameters are not checked for parameter contravariance #22156
Closed
@j-oliveras Jordi Oliveras Rovira (j-oliveras) mentioned this pull request Apr 30, 2018
Inline cast in function parameter #23773
Closed
@dgofman-equinix David Gofman (dgofman-equinix) mentioned this pull request Apr 30, 2018
Disable strictFunctionTypes in typescript microsoft/vscode#48959
Closed
@microsoft Microsoft (microsoft) locked and limited conversation to collaborators Jun 14, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Reviewers

@sandersn Nathan Shively-Sanders (sandersn) sandersn approved these changes
@RyanCavanaugh Ryan Cavanaugh (RyanCavanaugh) RyanCavanaugh left review comments
@mhegazy Mohamed Hegazy (mhegazy) Awaiting requested review from mhegazy
@DanielRosenwasser Daniel Rosenwasser (DanielRosenwasser) Awaiting requested review from DanielRosenwasser

Uh oh!

There was an error while loading. Please reload this page.

Assignees

No one assigned

Labels

None yet

Projects

None yet

Milestone

TypeScript 2.6

Development

Successfully merging this pull request may close these issues.

Uh oh!

There was an error while loading. Please reload this page.

16 participants

@ahejlsberg @sandersn @RyanCavanaugh @gcnew @ikokostya @pelotom @zpdDG4gta8XKpMCd @goodmind @weswigham @Igorbek @JsonFreeman @xnnkmd @styfle @mhegazy @msftclas @msftgits
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
You are visitor 01402503 — GeezerVison does not store the pages you look up.
Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.