-
Notifications
You must be signed in to change notification settings - Fork 12.6k
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
Suggestion: Allow string literals in index types #8336
Comments
There's a proposed type operator called type CreateDataFlow<Source extends {[key: memberof Source]: Observable<any>}, Sink extends {[key: memberof Sink]: Observable<any>}> =
(source: Source) => Sink; This requires a relatively unusual, recursive-looking, self reference in the constraints, e.g. |
Alright, I've made some tests: This: function test<T extends T>() {} Gives a 'circular reference' error. But this doesn't: function test<T extends T[]>() {}
test<number>(); Instead it gives the error "type And it is already possible to construct a self-referencing constraint that does not generate an error: function test<T extends T | T[]>() {}
test<number>(); // No error Now let's go back to the original intention. I'll try to rewrite it a bit differently: type SimilarMembersButWithType<Type, Container> = { [key: memberof Container]: Type };
type CreateDataFlow<Source extends SimilarMembersButWithType<Observable<any>, Source>, Sink extends SimilarMembersButWithType<Observable<any>, Sink> =
(source: Source) => Sink;
It would be interesting to think what else can be done with this pattern, one thing is using a union in the expected type to express possibilities of types: declare function func<T extends SimilarMembersButWithType<number | string, T>>(arg: T);
func({ a: 1, b: 2, c: "abc" }); // works;
func({ a: 1, b: 2, c: "abc", d: true }); // errors; Edit 1: I changed the alias name to Edit 2: corrected |
Sorry that this took so long to take a look at @christyharagan! There is actually another issue about this (#5683), so I'm going to close this as a duplicate, though I'm glad you took the time to give more motivating examples. Thanks! |
Problem
In the cycle.js project, data-flow is implemented by defining functions of the type:
Where
Source
andSink
are interfaces where every member is a property of typeObservable
.Each implementation of
CreateDataFlow
will specify Interfaces (typically without index types) forSource
andSink
.Currently, it is not possible to specify this contract using types.
This is a problem because the contract is not enforced by types, and instead relies on the developer to implement their data-flow function correctly. More specifically, this is a problem when dealing with higher-order code that takes implementations of
CreateDataFlow
, that wants to deal with the input/output generically (i.e. without knowing what the keys/members are at compile time, it wishes to iterate over them knowing only that the type isObservable
).Existing Solutions
There are two options:
The first is the one illustrated above. I.e., where the type-parameters
Source
andSink
have no type-restrictions. This creates the situation where any function that takes a parameter will be accepted. It's not clear from the typing what the contract should be, and so it will be trivially easy to pass in an invalidly typed function. If we wish to iterate over the output of the implementation, for example, we have to cast it to an index type (although, type guards do at least make this runtime safe).The second option is to try something like:
The problem is, this doesn't actually adhere to the contract: If we try to use an implementation where
Source
is an Interface (with all members having typeObservable
), an error is thrown by the compiler. Using this approach we are forced to weaken the contract to index-types only, which means we cannot differentiate (at a type-level) between implementations, and indeed allows callers of an implementation ofCreateDataFlow
to pass in any conforming instance (although, again type-guards do make this runtime safe).Neither solution adequately captures the contract, and requires use of type-guards to make type-safe. Needless to say, runtime type-safety is not nearly as nice as compile time, otherwise we'd all be using JavaScript instead.
Proposal: Allow string literals in index types
The proposal is that the following code would be valid:
And so the definition of
CreateDataFlow
becomes:Sub-types would be interfaces, and Key would be a string-literal defining the members, e.g.:
Potentially something akin to #7722 could be used to manage these "key" definitions.
The text was updated successfully, but these errors were encountered: