-
Notifications
You must be signed in to change notification settings - Fork 1
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
Can the documentation be made a little less opaque? #3
Comments
I guess what I'm not seeing is how the constraint itself can be an inferred generic (which receives arguments)? 🤔 In other words, say I have this interface: interface One<T extends Record<string, any>> {
data: T
} And then I make a new interface like: interface NewOne<T extends Record<string, any>> extends One<T> {
data2: T
} Then I have a function like: const fn = <T extends One[]>(plugins: T, data: Record<string, any>) => ... This is an oversimplification, but I'd like the resulting output to be strongly typed based on data passed into the function, in other words, I want to iterate through |
Hi matthew-dean, Thanks a lot for your feedback. Despite my best efforts I was indeed a bit expeditious in some places and I am relatively blind to what needs to be explained. A word of warning: free-types do not allow you to infer things which are otherwise not inferable. They simply give you a way to articulate "type constructor" or "type parameter" separately when you write a type (to clarify: by providing TS identifiers which target the constructor and the argument separately, they can indeed help infer things, but you won't overcome compiler limitations since free-types are written in TS). When I talk about "arguments" and "parameters" I mean the arguments and parameters of the free type constructor. They are distinct from type parameters the same way type parameters are distinct from function parameters, it's another level of abstraction, but it is the same metaphor. You can find an example of how to connect the value level and the type level in the Dependency Inversion section of the Use Cases. which gives an example of how you can infer a free type constructor from a function in a higher order function. I am completely rewriting the documentation for the next major version (and including a healthy dose of JSDoc). -- |
Regarding your use case, I am not certain free-types are necessary. Could you be more precise or link to a stackoverflow question? |
Hmm @geoffreytools - I found this library from a post you made for this issue, in which there's a proposal to be able to pass generics into types, so that you can pass type parameters into those generics. You wrote:
So, I inferred that you had found a way to solve this issue (at least partially), that is, the issue of being able to pass generics into types and thus dynamically pass type args into those generics. Are you saying that isn't the case? |
People have weird expectations regarding HKT and it is not clear what you need to do, so I am testing the water ;) You can find bellow an example derived from your code where import { Type, A, apply, unwrap } from 'free-types';
/** add `data` to elements of the plugins array */
const merge = <const T extends readonly Plugin[], const D extends Data>(plugins: T, data: D) =>
plugins.map(plugin => ({
data: { ...plugin.data, ...data },
...('data2' in plugin ? { data2: { ...plugin.data2, ...data } } : {})
})) as Merge<T, D>;
/* the Merge utility type */
type Merge<T extends readonly Plugin[], D extends Data> = {
[K in keyof T]: apply<unwrap<T[K], [$One, $NewOne]>['type'], [Override<T[K]['data'], D>]>;
}
type Override<A, B> = unknown & {
[K in keyof A | keyof B]:
K extends keyof A & keyof B
? A[K] | B[K]
: K extends keyof B
? B[K]
: K extends keyof A
? A[K]
: never;
};
/* your types */
type Data = Record<string, any>;
type Plugin = One<Data> | NewOne<Data>;
interface One<T extends Data> {
data: T
}
interface NewOne<T extends Data> extends One<T> {
data2: T
}
/* value constructors */
const one = <const D extends Data>(data: D): One<D> => ({ data });
const newOne = <const D extends Data>(data: D): NewOne<D> => ({ data, data2: data });
/* free type constructors */
interface $One extends Type<[Data]> { type: One<A<this>> }
interface $NewOne extends Type<[Data]> { type: NewOne<A<this>> }
/* execution */
const r = merge([one({ a: 1 }), newOne({ b: 2 }), one({ c: 3 })], { d: 4 });
// ^? const r: readonly [One<{ a: 1; d: 4; }>, NewOne<{ b: 2; d: 4; }>, One<{ c: 3; d: 4; }>]
console.log(r);
/*
[{
data: { a: 1, d: 4 }
}, {
data: { b: 2, d: 4 },
data2: { b: 2, d: 4 }
}, {
data: { c: 3, d: 4 }
}]
*/ Now do you really need higher kinded types for this (as in: it would not be possible without free-types)? No, you could use conditional types to match elements of your tuple against This is one of these instances where I can say free-types don't allow you to infer things which are not otherwise inferable: I had to provide I hope this answer helps. I haven't touched TS in months so my fingers are a little numb. Please keep adding points which you find frustrating or unclear about the way the lib is presented, it is very enlightening. |
I think this library is one that I'm looking for, but I'm finding it extremely difficult to reason about the core types and the problems they solve. Mostly what's missing are examples, like real-world data where the resulting type is narrowed (or expanded? 🤔 ) by using a free type in a way that TS's utility types don't do.
For example, in the documentation, you jump straight into
Type
, and it says we're typing arguments. Arguments of what? A function? A function defined where? How does this type affect the input and output types of a given function? How do functions that have a type with justInput
inType
differ from those withOutput
?I feel like this is somewhat like allowing generics to infer generics, but I can't really tell. I know it's difficult to write documentation be putting aside that you already know exactly what's happening, but even with an advanced knowledge of TypeScript types, I feel like there isn't a lot of bridging to what this library is for and how it's used.
The text was updated successfully, but these errors were encountered: