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

Narrow types of observables in conditions #132

Open
tscpp opened this issue Jun 14, 2024 · 2 comments
Open

Narrow types of observables in conditions #132

tscpp opened this issue Jun 14, 2024 · 2 comments
Assignees
Labels
bug Something isn't working z: analyzer

Comments

@tscpp
Copy link
Owner

tscpp commented Jun 14, 2024

import * as ko from 'knockout'

export default {
  name: ko.observable<string | undefined>(),
};
<!-- ko if: name() !== undefined -->
  Hello <span data-bind="text: name()"></span>!
                               ^
                               "string | undefined" is not assignable to "string"
<!-- /ok if -->
@tscpp tscpp converted this from a draft issue Jun 14, 2024
@tscpp tscpp added the bug Something isn't working label Jun 14, 2024
@tscpp
Copy link
Owner Author

tscpp commented Jun 14, 2024

Simply unwrapping the observable won't work because the observable is a function. According to typescript, functions are nondeterministic, meaning that we can't expect the same return value.

const foo = ko.observable<string | undefined>();

if (foo() !== undefined) {
  const bar = foo();
  bar // string | undefined
}

The if/ifnot bindings should return a new child context where it extracts all the truthy values from the observable.

declare function binding<T>(value: ko.Observable<T>): ko.Observable<Extract<T, {}>>;

const foo = ko.observable<string | undefined>();
const bar = binding(foo); // extracts thruthy values
const baz = bar();
baz // string

@tscpp
Copy link
Owner Author

tscpp commented Jun 14, 2024

This is a problem because we can't know what condition is passed to the if/ifnot bindings. In the below example, we expect name to be ko.Observable<string> since we checked if the value is not null.

<!-- ko if: name() !== null -->
  Hello <span data-bind="text: name"></span>!
<!-- /ko -->

The below example unfortunately does not work since foo will only recieve an overload. So the type will become (): string | undefined & (): string. The return value from this type is string | undefined. In conclusion, asserting the observable will not work.

const foo = ko.observable<string | undefined>();
assert<ko.Observable<string>>(foo);

const bar = foo();
bar // string | undefined

A solution to this would be to assert all instances of the observable in the descendants of the binding. The downside is that this would probably require a lot of work and slow down the transpiler even more.

const foo = ko.observable<string | undefined>();

const bar = foo() as string;
bar // string

Issue is open to suggestions!

@tscpp tscpp changed the title if/ifnot bindings does not unwrap observables Narrow types of observables in conditions Jun 14, 2024
@tscpp tscpp moved this from Todo to In Progress in Knuckles Roadmap 🚧 Jun 15, 2024
@tscpp tscpp moved this from In Progress to Todo in Knuckles Roadmap 🚧 Jun 15, 2024
@tscpp tscpp self-assigned this Jun 15, 2024
@tscpp tscpp moved this from Todo to In Progress in Knuckles Roadmap 🚧 Jun 15, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working z: analyzer
Projects
Status: In Progress
Development

No branches or pull requests

1 participant