question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Improve type inference for circular references in object initializers

See original GitHub issue

When you have something like this (also see a playground):

function makeFoo<T>(fn: () => T): () => T {
  // do some work here ...
  return function() {
    // ... or here
    return fn() 
  }
}

const obj = {
  name: 'test',

  foo: makeFoo(() => obj.name.length)
}

type of obj will be infered as any, because in order to determine type of obj typescript first looks at return type of makeFoo(...) which uses obj.

The actual error is this: 'obj' implicitly has type 'any' because it does not have a type annotation and is referenced directly or indirectly in its own initializer.(7022)

What if we could give some hint to a compiler to handle this case? Maybe like this:

const obj = {
  name: 'test',

  foo: makeFoo(() => obj.name.length) as prop  // or a better name
}

Right now I have a workaround for this:

function getter<T, R>(t: T, k: keyof T, value: R) {
  Object.defineProperty(t, k, {value, enumerable: true})  
  return value
}

const obj = {
  name: 'test',

  get foo() {
    return getter(obj, 'foo', makeFoo(() => obj.name.length))
  }
}

but it would be cool if it could be done more easily.

Issue Analytics

  • State:open
  • Created 2 years ago
  • Reactions:4
  • Comments:12 (6 by maintainers)

github_iconTop GitHub Comments

1reaction
theScottyJamcommented, Jan 26, 2022

this isn’t really compatible with the straightforward caching recursive descent

Yeah, this makes sense. TypeScript uses a recursive descent parsing algorithm, and the sort of algorithm I was describing would require an entirely different type of parsing algorithm. Not only would it take a whole lot of work to switch, it’ll also add a lot of complexity to the codebase, and this particular issue isn’t an extremely common one.

I don’t think I’ll try to actually make a PR - I can tell that this sort of thing would be a giant refactor, and it’s certainly not a task I would be capable of, especially considering that I currently know nothing about the TypeScript codebase.

Ok, thanks for helping me understand 😃

1reaction
RyanCavanaughcommented, Jan 21, 2022

The mechanism you’re describing here where, upon detecting a possible circularity, we just sort of put the relevant question on pause and come back to it later - this isn’t really compatible with the straightforward caching recursive descent that the checker is written in.

It’s instructive to look at the call stack at the point that we log the circularity error and see how every caller in that path (of which the incoming call stack is could be practically any function in the checker) would have to be basically completely rewritten to handle the answer of “ask me again later”.

We also need to be able to detect circularities instead of looping forever. For example, if you wrote

function f1() {
  return [0, f2()] as const;
}
function f2() {
  return [0, f1()] as const;
}

then any sort of work-deferring mechanism needs to be able to detect when it’s looping forever so that it doesn’t attempt to infer an infinite sequence of [0, [0, [0, [0, [0, [0, ....

The relevant codepaths in the checker here are not especially complicated (relatively speaking) and I’d encourage you to try a PR to understand the problem more deeply. Not to be glib, but if this were low-hanging fruit, we would have done it already 😉. Again, it’s not impossible, but it’s not at all a straightforward fix - we’d have to rewrite very large portions of the entire checker.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Swift Lazy Property Initialization Circular Reference ...
The circular reference is in the type inferencing: It's the fact that foo 's type depends on self.foo 's type, which depends on...
Read more >
Dealing with circular type references in Mobx-state-tree
The idea is to augment the inferred type of Snippet$1 model with a manual specification of types of attributes which cause circular reference....
Read more >
Remove or allow a circular reference - Microsoft Support
If you're using Excel 2010 or later, click File > Options > Formulas. · In the Calculation options section, select the Enable iterative...
Read more >
Custom pluggable types for ... - The Checker Framework Manual
31.1 Invariant array types; 31.2 Context-sensitive type inference for ... indicating that ref must be a reference to a non-null object.
Read more >
numba.ast_type_inference — numba 0.5.0 documentation
Initialize with a minivect context, a Python ast, and a function type with a given or ... Raise an exception for a circular...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found