-
Notifications
You must be signed in to change notification settings - Fork 64
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
Support placeholders and selectable-overloads #173
Comments
Hey, thanks for making this thread. You've touched upon both sources for typing variations per function: both actual variations (normally based on input types), and currying variations. In that first category, your I liked the idea of codegen to deal with currying as well, and though it wasn't fully finished, a current attempt at doing that did include code for Out of curiosity, would you mind commenting on the use-case / context where 0-param application became a point for you? I realize it's valid Ramda. That said, I do feel concerned about the utility vs. cost economics of this feature, both for the per-function code on our end (comparing the fragments required to generate the code), as well as for users (code without explicit flags still works, right?). Overall, the topic you're bringing up is actually pretty interesting. I could, for example, imagine a different variation than you've mentioned so far: using e.g. On enabling 0-param application, I wonder if there may be an easier way, kind of like: interface ZeroEnabled<F> {
() => F;
F;
} ... or, well, any actually working simple-ish alternative in that fashion, whatever that might be... |
usecase of selectable overloads
Yes, it will return itself but with the specified overload used only.
So I put the selectable overloads before the last case, TS will select the last case while it cannot determine which to use, correct me if I am wrong. type adjust_100<T, U> = {
(index: number, array: T[]): adjust_111<T, U>;
(_index: PH, array: T[]): adjust_101<T, U>;
<X extends "11">(): (index: number, array: T[]) => adjust_111<T, U>;
<X extends "01">(): (_index: PH, array: T[]) => adjust_101<T, U>;
<X extends "1">(): (index: number) => adjust_110<T, U>; // before last case
(index: number): adjust_110<T, U>; // TS will select the last case while cannot be determined
}; Actually, I have tried to build my own @ikatyang/types-ramda at the begining with just placeholders supported, but I had faced to a problem like: declare function example<R>(fn: (...args: any[]) => R): R; while passing the curried function like example(R.adjust) //=> ???
example(R.adjust<"11">()) //=> predictable So I started to create dts-element for generating the dts structurally. readability for codegen vs manualAs you can see in the first comment, the following will generate the ugly types. const types = dts.create_curried_function_types({
name,
placeholder,
type: function_type,
selectable: true,
});
const document = new dts.Document({children: types});
console.log(document.emit()); //=> type adjust_XXX ... It is not just for generating curried types, it can generate all valid dts, the following will generate the original type const type_declaration = new dts.TypeDeclaration({
name,
type: function_type,
});
const document = new dts.Document({children:[type_declaration]});
console.log(document.emit()); //=> type adjust = <T, U>(fn: (v: T) => U, index: number, array: T[]) => (T | U)[] It is easily to switch between ugly-curried and original cases. About ZeroEnabled
Edit interface ZeroEnabled<F> {
(): F;
F; // <--- invalid...
} Edit And also, the interface version with placeholder (also auto generated): interface CurriedFunction3<T1, T2, T3, R> {
(v1: T1, v2: T2, v3: T3): R;
(v1: T1, _2: PH, v3: T3): CurriedFunction1<T2, R>;
(_1: PH, v2: T2, v3: T3): CurriedFunction1<T1, R>;
(_1: PH, _2: PH, v3: T3): CurriedFunction2<T1, T2, R>;
(v1: T1, v2: T2): CurriedFunction1<T3, R>;
(_1: PH, v2: T2): CurriedFunction2<T1, T3, R>;
(v1: T1): CurriedFunction2<T2, T3, R>;
} |
I'm sorry, I finally understood the problem you're tackling is not 0-param calls, but Ramda placeholder (
On second glance, I think the data structure you use for codegen isn't so difference from the one I'd added in
Yeah, that sounds correct. So that addresses my concern about UX -- thanks. I'm starting to get where you're going about using generics over overloads using placeholder type, given you're not immediately passing parameters. I suppose in the use-case where parameters had been passed, one would probably want to handle placeholders with overloads with placeholder type, so that no generics would need to be manually specified. I guess with my idea of putting variations e.g.
Complication: curried variants need to be mixed into one and the same interface, or TS would ignore all but the first one. I haven't tested your approach, but it's something to keep in mind here. This particular point was currently handled in my codegen in
Yeah, I know, just tried to give the simplest version I could think of. But now I realize enabling 0-param application wasn't really your goal, just a means to pre-selecting placeholder variations for use in un-applied functions. I'm initially under the impression unapplied placeholders seem a bit of a niche (compared to applying them right away); otherwise I'm under the impression there are no real blockers here though, so if we could combine the advantages of our codegen methods (low verbosity, dealing with placeholders / currying / mixing partially applied variations), then that would be great. |
Did you mean the following code? interface CurriedFunction3<T1, T2, T3, R> {
(v1: T1, _2: PH, v3: T3): CurriedFunction1<T2, R>;
(_1: PH, v2: T2, v3: T3): CurriedFunction1<T1, R>;
(_1: PH, _2: PH, v3: T3): CurriedFunction2<T1, T2, R>; // above the normal version
(v1: T1, v2: T2, v3: T3): R;
(_1: PH, v2: T2): CurriedFunction2<T1, T3, R>; // above the normal version
(v1: T1, v2: T2): CurriedFunction1<T3, R>;
(v1: T1): CurriedFunction2<T2, T3, R>;
} If so, it would be a strange UX but will work perfectly with UX vs Correctness, I think correctness is better.
append: [
['T', 'U'],
{
el: 'U',
list: 'List<T>',
},
'(T & U)[]'
]
EDIT: Sorry, I forgot the regex
Sorry, I just forgot this case. Since I can generate it manually, so I think it can be generated programmatically, I'll try it later. manual: type map_11_array<U> = U[];
type map_11_mappable<U> = Mappable<U>;
type map_10_array<T, U> = {
(array: T[]): map_11_array<U>;
};
type map_10_mappable<T, U> = {
(mappable: Mappable<T>): map_11_mappable<U>;
};
type map_10<T, U> = {
(array: T[]): map_11_array<U>;
<K extends 'array'>(): map_10_array<T, U>;
<K extends 'mappable'>(): map_10_mappable<T, U>;
(mappable: Mappable<T>): map_11_mappable<U>;
};
type map_01_array<T> = {
<U>(fn: (v: T) => U): map_11_array<U>
};
type map_01_mappable<T> = {
<U>(fn: (v: T) => U): map_11_mappable<U>
};
type map_00_array = {
<T, U>(fn: (v: T) => U, array: T[]): map_11_array<U>;
<T>(_fn: PH, array: T[]): map_01_array<T>;
<X extends "11">(): <T, U>(fn: (v: T) => U, array: T[]) => map_11_array<U>;
<X extends "01">(): <T>(_fn: PH, array: T[]) => map_01_array<T>;
<X extends "1">(): <T, U>(fn: (v: T) => U) => map_10_array<T, U>
<T, U>(fn: (v: T) => U): map_10_array<T, U>;
};
type map_00_mappable = {
<T, U>(fn: (v: T) => U, mappable: Mappable<T>): map_11_mappable<U>;
<T>(_fn: PH, mappable: Mappable<T>): map_01_mappable<T>;
<X extends "11">(): <T, U>(fn: (v: T) => U, mappable: Mappable<T>) => map_11_mappable<U>;
<X extends "01">(): <T>(_fn: PH, mappable: Mappable<T>) => map_01_mappable<T>;
<X extends "1">(): <T, U>(fn: (v: T) => U) => map_10_mappable<T, U>;
<T, U>(fn: (v: T) => U): map_10_mappable<T, U>;
};
type map_00 = {
<T, U>(fn: (v: T) => U, array: T[]): map_11_array<U>;
<T, U>(fn: (v: T) => U, mappable: Mappable<T>): map_11_mappable<U>;
<T>(_fn: PH, array: T[]): map_01_array<T>;
<T>(_fn: PH, mappable: Mappable<T>): map_01_mappable<T>;
<K extends 'array', X extends "11">(): <T, U>(fn: (v: T) => U, array: T[]) => map_11_array<U>;
<K extends 'array', X extends "01">(): <T>(_fn: PH, array: T[]) => map_01_array<T>;
<K extends 'array', X extends "1">(): <T, U>(fn: (v: T) => U) => map_10_array<T, U>
<K extends 'array'>(): map_00_array;
<K extends 'mappable', X extends "11">(): <T, U>(fn: (v: T) => U, mappable: Mappable<T>) => map_11_mappable<U>;
<K extends 'mappable', X extends "01">(): <T>(_fn: PH, mappable: Mappable<T>) => map_01_mappable<T>;
<K extends 'mappable', X extends "1">(): <T, U>(fn: (v: T) => U) => map_10_mappable<T, U>;
<K extends 'mappable'>(): map_00_mappable;
<T, U>(fn: (v: T) => U): map_10<T, U>;
};
It seems it just mixin the first layer, for example: type A = {
(): {
(): string;
};
(): number;
};
type B = {
(): {
(): symbol;
};
(): boolean;
};
type C = A & B;
declare const c: C;
c()() //=> string |
Yeah, both
I ran into exactly that in the current implementation. I just check when we get to new parameters (using a non-placeholder, of course) -- if these contain (= case-sensitive full-word regex match) one of the generics not used thus far, then now it's used, so should be added.
Hm, yeah, looks like merging typings using just I've now added what's generated by the current codegen so as to show current progress -- the intent being for it to replace the manually maintained file somehow (wouldn't mind a copy-paste, just wanted for contributors not to have to deal with the currying). |
Isn't it EDIT: for function and<T, U>(a: T, b: U): T | U; since the source code was just I always look at the source code and think about what function signature should be, maybe I should record all the signature of
I'd suggest making some scripts for copy-paste automatically, since copy-paste manually may make some mistakes. I've done my codegen for merged version and fix the problem of ordering, but I think there may be some bugs since I haven't tested it in every case. various type map = map_00;
type map_00 = {
<T>(_fn: PH, mappable: Mappable<T>): map_mappable_01<T>;
<T>(_fn: PH, array: T[]): map_array_01<T>;
<T, U>(fn: (v: T) => U, array: T[]): map_array_11<T, U>;
<T, U>(fn: (v: T) => U, mappable: Mappable<T>): map_mappable_11<T, U>;
<K extends "array", X extends "01">(): <T>(_fn: PH, array: T[]) => map_array_01<T>;
<K extends "mappable">(): map_mappable_00;
<X extends "1">(): <T, U>(fn: (v: T) => U) => map_10<T, U>;
<K extends "array">(): map_array_00;
<K extends "array", X extends "11">(): <T, U>(fn: (v: T) => U, array: T[]) => map_array_11<T, U>;
<K extends "mappable", X extends "11">(): <T, U>(fn: (v: T) => U, mappable: Mappable<T>) => map_mappable_11<T, U>;
<K extends "mappable", X extends "01">(): <T>(_fn: PH, mappable: Mappable<T>) => map_mappable_01<T>;
<T, U>(fn: (v: T) => U): map_10<T, U>;
};
type map_10 = {
(array: T[]): map_array_11<T, U>;
<K extends "array">(): (array: T[]) => map_array_11<T, U>;
<K extends "mappable">(): (mappable: Mappable<T>) => map_mappable_11<T, U>;
(mappable: Mappable<T>): map_mappable_11<T, U>;
};
type map_array_00 = {
<T>(_fn: PH, array: T[]): map_array_01<T>;
<T, U>(fn: (v: T) => U, array: T[]): map_array_11<T, U>;
<X extends "11">(): <T, U>(fn: (v: T) => U, array: T[]) => map_array_11<T, U>;
<X extends "01">(): <T>(_fn: PH, array: T[]) => map_array_01<T>;
<X extends "1">(): <T, U>(fn: (v: T) => U) => map_array_10<T, U>;
<T, U>(fn: (v: T) => U): map_array_10<T, U>;
};
type map_array_01<T> = {
<U>(fn: (v: T) => U): map_array_11<T, U>;
};
type map_array_10<T, U> = {
(array: T[]): map_array_11<T, U>;
};
type map_array_11<T, U> = U[];
type map_mappable_00 = {
<T>(_fn: PH, mappable: Mappable<T>): map_mappable_01<T>;
<T, U>(fn: (v: T) => U, mappable: Mappable<T>): map_mappable_11<T, U>;
<X extends "11">(): <T, U>(fn: (v: T) => U, mappable: Mappable<T>) => map_mappable_11<T, U>;
<X extends "01">(): <T>(_fn: PH, mappable: Mappable<T>) => map_mappable_01<T>;
<X extends "1">(): <T, U>(fn: (v: T) => U) => map_mappable_10<T, U>;
<T, U>(fn: (v: T) => U): map_mappable_10<T, U>;
};
type map_mappable_01<T> = {
<U>(fn: (v: T) => U): map_mappable_11<T, U>;
};
type map_mappable_10<T, U> = {
(mappable: Mappable<T>): map_mappable_11<T, U>;
};
type map_mappable_11<T, U> = Mappable<U>; normal type adjust = adjust_000;
type adjust_000 = {
<T, U>(fn: (v: T) => U, _index: PH, array: T[]): adjust_101<T, U>;
<T>(_fn: PH, index: number, array: T[]): adjust_011<T>;
<T>(_fn: PH, _index: PH, array: T[]): adjust_001<T>;
<T, U>(fn: (v: T) => U, index: number, array: T[]): adjust_111<T, U>;
(_fn: PH, index: number): adjust_010;
<T, U>(fn: (v: T) => U, index: number): adjust_110<T, U>;
<X extends "1">(): <T, U>(fn: (v: T) => U) => adjust_100<T, U>;
<X extends "101">(): <T, U>(fn: (v: T) => U, _index: PH, array: T[]) => adjust_101<T, U>;
<X extends "011">(): <T>(_fn: PH, index: number, array: T[]) => adjust_011<T>;
<X extends "001">(): <T>(_fn: PH, _index: PH, array: T[]) => adjust_001<T>;
<X extends "11">(): <T, U>(fn: (v: T) => U, index: number) => adjust_110<T, U>;
<X extends "01">(): (_fn: PH, index: number) => adjust_010;
<X extends "111">(): <T, U>(fn: (v: T) => U, index: number, array: T[]) => adjust_111<T, U>;
<T, U>(fn: (v: T) => U): adjust_100<T, U>;
};
type adjust_001<T> = {
(_fn: PH, index: number): adjust_011<T>;
<U>(fn: (v: T) => U, index: number): adjust_111<T, U>;
<X extends "11">(): <U>(fn: (v: T) => U, index: number) => adjust_111<T, U>;
<X extends "01">(): (_fn: PH, index: number) => adjust_011<T>;
<X extends "1">(): <U>(fn: (v: T) => U) => adjust_101<T, U>;
<U>(fn: (v: T) => U): adjust_101<T, U>;
};
type adjust_010 = {
<T>(_fn: PH, array: T[]): adjust_011<T>;
<T, U>(fn: (v: T) => U, array: T[]): adjust_111<T, U>;
<X extends "11">(): <T, U>(fn: (v: T) => U, array: T[]) => adjust_111<T, U>;
<X extends "01">(): <T>(_fn: PH, array: T[]) => adjust_011<T>;
<X extends "1">(): <T, U>(fn: (v: T) => U) => adjust_110<T, U>;
<T, U>(fn: (v: T) => U): adjust_110<T, U>;
};
type adjust_011<T> = {
<U>(fn: (v: T) => U): adjust_111<T, U>;
};
type adjust_100<T, U> = {
(_index: PH, array: T[]): adjust_101<T, U>;
(index: number, array: T[]): adjust_111<T, U>;
<X extends "11">(): (index: number, array: T[]) => adjust_111<T, U>;
<X extends "01">(): (_index: PH, array: T[]) => adjust_101<T, U>;
<X extends "1">(): (index: number) => adjust_110<T, U>;
(index: number): adjust_110<T, U>;
};
type adjust_101<T, U> = {
(index: number): adjust_111<T, U>;
};
type adjust_110<T, U> = {
(array: T[]): adjust_111<T, U>;
};
type adjust_111<T, U> = (T | U)[]; interfaces interface CurriedFunction1<T1, R> {
(v1: T1): R;
}
interface CurriedFunction2<T1, T2, R> {
(_1: PH, v2: T2): CurriedFunction1<T1, R>;
(v1: T1, v2: T2): R;
(v1: T1): CurriedFunction1<T2, R>;
}
interface CurriedFunction3<T1, T2, T3, R> {
(v1: T1, _2: PH, v3: T3): CurriedFunction1<T2, R>;
(_1: PH, v2: T2, v3: T3): CurriedFunction1<T1, R>;
(_1: PH, _2: PH, v3: T3): CurriedFunction2<T1, T2, R>;
(v1: T1, v2: T2, v3: T3): R;
(_1: PH, v2: T2): CurriedFunction2<T1, T3, R>;
(v1: T1, v2: T2): CurriedFunction1<T3, R>;
(v1: T1): CurriedFunction2<T2, T3, R>;
} How can I do for this repo now? It is difficult for me to write the scripts (FP) here, since I am new to ramda and FP ( I just read the docs and haven't used it... ). Should I generate the dts using my version and make some PR or .. ? |
Both
Yeah, fair enough.
If you've already generated the For the full typings, before I could merge a PR I'm thinking we'd probably need to finish the generated typings over here first so that we could test for the differences between them. I don't have much time for open-source recently though, so I realize that isn't helping much... |
add: {
base: [
['T extends {and?: Function}'], // <--- I thought this is not for add...
{
fn1: 'T',
val2: 'boolean+any'
},
'boolean'
],
no_generics: [
[],
{
v1: 'any',
v2: 'any',
},
'boolean',
]
},
I thought that just updating the I'll try the full typings myself in my repo first, and we can mix them together or do something else while you have time to do such work :) |
Ouch, crap, thanks for noticing! Fixed now.
I know it'd only add partial support, but I'd think it'd still help a bit already, no? Anyway, no strong preference here -- as you prefer.
Fair enough, sorry for the trouble! P.S.: Saw you're from Taiwan; pretty cool place. :D |
On that one, well... I used to have similar codegen for the I think a similar strategy could work well here. Fortunately, I think Ramda probably doesn't even really have many functions that have many parameters... so I imagine we could probably get away with ignoring the longer ones. |
Curried1: 1 rows I prefer 1 ~ 7 or 1 ~ 8, what do you think? |
My initial reflex would be toward 3-6 -- I feel a bit hesitant toward 7-9, since versions over 3 don't seem likely to be used much in practice (only in That could become a concern for other typings with placeholder support as well. It'd be unfortunate if non-users would fall victim to that, so perhaps that could merit separate branches. Please feel free to experiment. Hopefully things will be fine. |
I just rewrote my whole codegen library dts-element for v2, it is available to parse dts by using TS API now, so that my codegen is easily to understand now, and it will be restructured into this. And I wrote a snapshot tester dts-jest base on jest and typings-checker, so that it will be easily to test with watching mode. And I realize that there are some strange(?) typings in this repo that I can't just copy all of them, such as R.and, there is no Additionaly, Ramda v0.24.1 had just released. Here's my progress now, any suggestion? |
Whoops, seems you're right the dispatching for Thanks for noting the Ramda update as well; I've now opened #181 for that. In #165 @whitecolor had also pointed out to me that it'd be of importance to add run-time testing so as to ensure run-time / compile-time alignment. And that's fair. Could you perhaps comment a bit on how you see That said, the @whitecolor: since you had also suggested a new code-gen approach there, could you weigh in as well (and maybe vice-versa)? Hopefully we'll end up with the best of three worlds. To try and summarize considerations contributed / emphasized: @whitecolor (taken verbatim from #165):
Also covered:
Others I'm making out from the code (could you comment on these?)
Some comments I had on @whitecolor's approach:
Some questions on yours:
On getting the other 90% of the Ramda typings covered in a scalable manner, might it be viable to parse from these so you could focus efforts could be focused on the universal logic (be it from this starting point, the universal utils you already had down, or anything in between)? I hope that would help make placeholder support a somewhat less colossal task... Feel free to comment. That said, I'd suggest also still trying to check performance when used in projects, to ensure you wouldn't run into issues like the one I had with Hopefully in the future TS would get powerful enough that we could do dynamic compile-time calculation to handle complexity (typing variants, currying, placeholders) rather than having massive overloads per function typing, but it's not quite there yet... |
I used to think that I just have to check its type, but after seeing #165 I realized that in some cases may cause problems, just like
The reason I created In For the version-controlled diff log, you can use And yes, there is also Additionally, I think the testing in this repo now is somehow weird, I can't understand why
I think TS is smart to choose correct typings, and if it can't, just use the selectable overload.
Well, if you have the standard naming and does not mind to have consistent naming that I have said above, I can just follow that and remove the abstract naming.
I'll try it later, thanks for pointing out.
I have no idea what problem you had faced to
Yes, I hope so too, but they are still in the Future roadmap, hope to see it in some version's release note someday. |
Yeah Travis is still broken. Thanks for elaborating on the other points. The Jest snapshots look pretty interesting! On overload order w.r.t. placeholders, I'm under the impression we may still have a problem: var placeholder: { placeholder: true };
var any: any;
var object: object;
declare function foo(v: { placeholder: true }): 0;
declare function foo(v: object): 1;
var a = foo(placeholder); // 0
var b = foo(any); // 0 :(
var c = foo(object); // 1
declare function bar(v: object): 1;
declare function bar(v: { placeholder: true }): 0;
var d = bar(placeholder); // 1 :(
var e = bar(any); // 1
var f = bar(object); // 1 With I also eagerly await the horizon of the TS roadmap. :) |
Yes, it is. I think that is the pain point we cannot resolve it without selectable overloads, since one is OK then another will be not OK. We can only choose one to be the general case. type PH = { placeholder: true };
declare const placeholder: PH;
declare const any: any;
declare const object: object;
declare const foo: {
(v: PH): 0;
<X extends "0">(): (v: PH) => 0;
<X extends "1">(): (v: object) => 1;
(v: object): 1;
}
var a = foo(placeholder); // 0
var b = foo<"1">()(any); // 1 :)
var c = foo(object); // 1 Summary
Let me know if this is correct or not :) |
I think tests here had originally been based on those in the Ramda repo (+ currying, type/error assertions), though not kept in sync at least -- I haven't looked at the original tests, for one. I guess they were still missing run-time assertions though, yeah.
I guess with 3, 2 should be covered. I guess for the purpose of handling placeholder support we could probably put off the refactor though? I guess what I'm trying to get at with that is I like the flexibility of the current notation, with separated data/logic.
Well, I suppose if we have run-time tests, then ones for like
Ideally, though leaving it to its own separate issue is fine as well.
That's already a lot of points primarily just for placeholder support. :) |
I've almost done my types-ramda, so I think it's time to get some feedback. Done
See templates/map.d.ts -> src/map.d.ts and templates/README.md for example.
I have looked every typings and migrate them one by one, and rewrite some of them if necessary. There are several wrong types in this repo and even wrong test cases, such as
I've also done the new types for v0.23.0...v0.24.1, and some of them have been removed by Ramda, such as Not Yet
It is easy to compile my own exist tests for DT compatible, but I have no idea if I should do so, since it will be a lot of breaking changes for placeholder support. Let me know what you think about this. And if you think it is time to send a fully rewrite PR to this repo, I am happy to do so. EDIT: There are several functions that I have no idea how to integration test them, since they are all |
Tests:Using the markdown files to take care of the comments looks awesome! That definitely makes for a much better editing experience than the previous ideas there like manual comments / strings.
The As to tests, I feel like swapping them out in one go is a bit unfortunate in the sense it makes it harder to compare the situation before and after this PR. Might it be possible to run the current test suite on it as well so as to compare output? I realize we can make things DT-compatible by commenting anything not working well yet, but yeah, that's a separate concern I guess. Honestly I love how you lifted Ramda's JSDoc comments to get top-level support, and I wish we could similarly just provide an
So basically that's a consideration after 6606+5453 land. Code-gen:On separation of concerns with data vs. logic, it's interesting you picked a different path by manipulating typings directly rather than storing them as POJOs. I definitely see the beauty of your path there, as obviously any abstracting representation can be considered opinionated, while you've managed to pick a data representation that, for the purpose of abstracting over currying and overload selection, manages not to diverge from TS, which helps reduce learning curve for the functions that don't require further code-gen. I think for the purpose of the I guess conversion from such an abstracted representation could be centralized -- that way the That said, although I liked the compactness of POJOs and the ease with which they'd enable different code-gen approaches, I guess I've been feeling much more strongly about overcoming the need for codegen to make TS usable than about the specific representations used... On another note, it'd be cool to bootstrap the thing and eat our own dog-food by using generated typings to do the typing generation with Ramda in TS (might cut down on deps compared to lodash if those even get downloaded by users in the first place?), but yeah, not like Ramda itself looks pretty inside. ☺ tl;drIf we can test to verify semantics aren't adversely affected, and ideally get some anecdotal evidence compilation isn't horribly suffering from the added placeholder support even for non-users, then let's merge this into master here.
Could you elaborate on that? On Ramda-Fantasy typings, yeah, might need to import some other library for testing I guess. Or just get the minimum type definition / implementation needed to test them, like some |
I used to think about selectable-overloads with just type map_00 = {
<T>(_fn: PH, list: List<T>): map_list_01<T>;
<T>(_fn: PH, functor: Functor<T>): map_functor_01<T>;
<T, U>(fn: Morphism<T, U>, list: List<T>): map_list_11<T, U>;
<T, U>(fn: Morphism<T, U>, functor: Functor<T>): map_functor_11<T, U>;
<$SEL extends "11", $KIND extends "list">(): <T, U>(fn: Morphism<T, U>, list: List<T>) => map_list_11<T, U>;
<$SEL extends "11", $KIND extends "functor">(): <T, U>(fn: Morphism<T, U>, functor: Functor<T>) => map_functor_11<T, U>;
<$SEL extends "01", $KIND extends "list">(): <T>(_fn: PH, list: List<T>) => map_list_01<T>;
<$SEL extends "01", $KIND extends "functor">(): <T>(_fn: PH, functor: Functor<T>) => map_functor_01<T>;
<$SEL extends "1">(): <T, U>(fn: Morphism<T, U>) => map_10<T, U>;
+ <$KIND extends "list">(): {
+ <T>(_fn: PH, list: List<T>): map_list_01<T>;
+ <T, U>(fn: Morphism<T, U>, list: List<T>): map_list_11<T, U>;
+ <$SEL extends "01">(): <T>(_fn: PH, list: List<T>) => map_list_01<T>;
+ <$SEL extends "11">(): <T, U>(fn: Morphism<T, U>, list: List<T>) => map_list_11<T, U>;
+ <$SEL extends "1">(): (list: List<T>) => map_list_11<T, U>;
+ (list: List<T>): map_list_11<T, U>;
+ };
+ <$KIND extends "functor">(): {
+ <T>(_fn: PH, functor: Functor<T>): map_functor_01<T>;
+ <T, U>(fn: Morphism<T, U>, functor: Functor<T>): map_functor_11<T, U>;
+ <$SEL extends "01">(): <T>(_fn: PH, functor: Functor<T>) => map_functor_01<T>;
+ <$SEL extends "11">(): <T, U>(fn: Morphism<T, U>, functor: Functor<T>) => map_functor_11<T, U>;
+ <$SEL extends "1">(): (functor: Functor<T>) => map_functor_11<T, U>;
+ (functor: Functor<T>): map_functor_11<T, U>;
+ };
+ <$SEL extends "11">(): {
+ <T, U>(fn: Morphism<T, U>, list: List<T>) => map_list_11<T, U>;
+ <$KIND extends "list">(): <T, U>(fn: Morphism<T, U>, list: List<T>) => map_list_11<T, U>;
+ <$KIND extends "functor">(): <T, U>(fn: Morphism<T, U>, functor: Functor<T>) => map_functor_11<T, U>;
+ <T, U>(fn: Morphism<T, U>, functor: Functor<T>) => map_functor_11<T, U>;
+ };
+ <$SEL extends "01">(): {
+ <U>(fn: Morphism<T, U>): map_list_11<T, U>;
+ <$KIND extends "list">(): <U>(fn: Morphism<T, U>) => map_list_11<T, U>;
+ <$KIND extends "functor">(): <U>(fn: Morphism<T, U>) => map_functor_11<T, U>;
+ <U>(fn: Morphism<T, U>): map_functor_11<T, U>;
+ }
<T, U>(fn: Morphism<T, U>): map_10<T, U>;
};
type map_10<T, U> = {
(list: List<T>): map_list_11<T, U>;
- <$SEL extends "1", $KIND extends "list">(): (list: List<T>) => map_list_11<T, U>;
- <$SEL extends "1", $KIND extends "functor">(): (functor: Functor<T>) => map_functor_11<T, U>;
+ <$KIND extends "list">(): (list: List<T>) => map_list_11<T, U>;
+ <$KIND extends "functor">(): (functor: Functor<T>) => map_functor_11<T, U>;
(functor: Functor<T>): map_functor_11<T, U>;
};
type map_list_01<T> = {
<U>(fn: Morphism<T, U>): map_list_11<T, U>;
};
type map_functor_01<T> = {
<U>(fn: Morphism<T, U>): map_functor_11<T, U>;
};
type map_list_11<T, U> = U[];
type map_functor_11<T, U> = Functor<U>; So I decided to use the minimal case for the performance and consistency, that is,
The root cause why my integration test ramda-tests.ts is all-in-one file, is that TS had to parse a lot of And you can see the difference affects the types before/after the PR in the snapshot files, e.g. ikatyang/types-ramda@
There are some internal/helper functions in Ramda tests, so I think it might be hard to use their tests directly, but we can follow their release note to modify for the new/removed features after new version released.
The
Let's wait for that, I think it is the most important feature for FP.
That's the pain point I see every time, TS will infer their types based on their "order", that is, the following two cases will cause different results: // base type
declare function sort<T>(fn: Comparator<T, number>, list: List<T>): T[];
const byAge = R.ascend(R.prop('age'));
R.sort(byAge, people); //=> T === any
R.sort(R.__, people)(byAge); //=> T === Person Unfortunately, this is the advantage ( FP currying, target goes last ) and also the disadvantage ( the most specific goes last, while TS uses the first matched type to infer ), we have to ensure both side is compatible. It can easy fix something like
Is that you mean the difference between function expression and function declaration? If so, they are the same thing on the type-level.
Is that you mean the failing tests? Currently I just set them as
Yes, but I can't figure out what case will be, so I think this kind of issue should be resolved after someone reported.
It seems that the representation like your script.js is more reasonable, I'll improve those
I think we can place their repeated helper function to
Yes, it is. But it seems FP is not on their 1st level priority.
I'm afraid that might cause some problem due to our multi-version typings, and currently it was github-based installation so that I have to put template/generated code together, which I think it'd better to gitignore those generated types. It seems multi-branch is better: And yes, using Ramda to coding is always my goal, I'll switch to Ramda once I confirm that it won't cause problems.
Yes, but I think TS's emitted declaration file will be terrible if someone want to export curried function directly...
I'm not sure about this, but I think it might be, since DefinitelyTyped and
I used to write types for Summary
It seems very close to our goal now. :) |
Hm, right. I'm personally not super worried about the bits of collateral damage if it's still creating a significant net gain, but yeah, I'm flexible there.
What would be your take on that?
I think most of these cases are caused by typings using Detecting the conditions ( That said, I think this covers cases such as
Sorry, I may have worded this poorly. I'm not so much concerned about whether they're in one/multiple files, but rather that the new code-base has not been judged using the old tests, which makes it harder to tell whether any new issues might have popped up so far. For that purpose, it should be useful to try running the old test suite on your generated typings as well. You don't have to refactor them, but if running them on the new typings would take a multiple of the original compile time (since you're noting it has been taking time for you with a merged test file on your end already), in that case it may be a consideration to reserve a dedicated branch for placeholders. |
Sorry, I used to think the emitted declarations will be reflected ( emit whole CurriedFunction typings ), but it seems it'll just emit something like: (some-emitted.d.ts) export declare const emitted: CurriedFunction2<string, number, boolean>; I think this kind of output is OK.
function prop<T>(_key: PH, object: T): <K extends keyof T>(key: K) => T[K];
function prop<T, K extends keyof T>(key: K, object: T): T[K];
-function prop<T, K extends keyof T>(key: K): (object: T) => T[K];
-function prop<K extends string, T extends Record<K, any>>(_key: PH, object: T): (key: K) => T[K];
-function prop<K extends string, T extends Record<K, any>>(key: K, object: T): T[K];
function prop<K extends string>(key: K): <T extends Record<K, any>>(object: T) => T[K];
-declare function sort<T>(_fn: PH, list: List<T>): (fn: Comparator<T, number>) => T[];
-declare function sort<T>(fn: Comparator<T, number>, list: List<T>): T[];
-declare function sort<T>(fn: Comparator<T, number>): (list: List<T>) => T[];
+declare function sort<T>(_fn: PH, list: List<T>): <U extends T>(fn: Comparator<U, number>) => T[];
+declare function sort<T, U extends T>(fn: Comparator<U, number>, list: List<T>): T[];
+declare function sort<U>(fn: Comparator<U, number>): <T extends U>(list: List<T>) => T[];
-R.sort(byAge, people); //=> T === any
+R.sort(byAge, people); //=> T === Person
R.sort(R.__, people)(byAge); //=> T === Person
There is the old test, I just copy-paste and normalize/lint them so as to match my pattern. Additionally, I removed those issue-cases due to their incompleteness, some of them even does not have I haven't read those issues yet, so I may have to read them and make those test suites be in my tests.
There is a watching-mode build script in my repo, so I'm not worried about compile time, it's fast enough. And I think we only have to test the full-typed version, and those different version will just work fine. The most time consuming part is testing, it costs 4~6 sec per file, but it is OK since I'll only run the ramda-tests.ts and those changed type's unit test. ( NOTE that jest run tests parallelly ) I mean that if I use Ramda in my codegen, I have to make a local copy of Ramda definitions so as to run those code without errors just like TS does ( TS compile itself using its previous version ). I think I have some idea for that. :) |
Yeah, I'd gladly help out on anything in that area. Probably not many others who have been looking into that much anyway, haha.
Awesome; saw tests for
Yeah, I'd been too lazy to look into those w.r.t. types; I guess we should be able to learn their expected types by checking run-time output.
Compilation part or run-time part? |
I can't figure out what you mean about And I fixed those error types by using selectable-overloads, and then mark them as
It's runtime part (TS parses those generated
I always execute my The actual tests and type tests are actually executed separately. And actual-tests are super faster (maybe 5x) than type tests, so that even execute them simultaneously won't cause performance problem, it's just fast enough. |
Sorry, I'd checked here, no prob then.
Does that mean it checks type A > B / B < A? I ask because the string-based checks (using
I take it imperfect inference would be |
I checked their inferrence by using For checking both
It's just not in the same file now, so that we don't have to copy-paste the changed type manually, it'll be done by jest using before
after
|
Sorry for the delay, I was tidying my repos and finally done. For current types, is it OK to send a PR? or should we wait for TS? since I have no idea what should I do for those types that we can't solve now. |
Is it that bad now? Cuz I'm not expecting much there soon. I only recall you mention that |
I'm currently use The only thing I concerned is that the |
That isn't microsoft/TypeScript#15768 right? |
Yeah, it seems I have to make sure there is no regression, I'll copy them back or write a temporary generator for them anyway, and once I done I'll send a PR here. |
alright, cool. 😃 |
After seeing Sanctuary's TS definition, I think it'd be better to expose the "curryify" generator from types-ramda so that it can be reused everywhere to generate FP definitions, I think I'll create the |
Fair enough 👍
…On Jul 31, 2017 2:33 PM, "Ika" ***@***.***> wrote:
After seeing Sanctuary's TS definition, I think it'd be better to expose
the "curryify" generator from types-ramda so that it can be reused
everywhere to generate FP definitions, I think I'll create the
dts-element-fp first, and then making the PR here.
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#173 (comment)>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/AC6uxQy6SaKpN2-BC12EIJedC34cirtMks5sTXU-gaJpZM4NUOrw>
.
|
On the JSDoc thing, I presume that still depends on there being something JS-like you're appending them to? If it could work even for just types I'd be interested if I could use it for my type repo as well... |
Uh, what did you mean for the JSDoc thing? |
I recall you'd gen JSDoc entries with md snippets. I was just curious if that might work for |
It just parse using dts-element, bind JSDoc then emit, for example: import * as dts from 'dts-element';
const definition = `
declare const x: number;
declare const y: boolean;
`;
const jsdocs = ['jsdoc for x', 'jsdoc foy y'];
const top_level_element = dts.parse(definition);
top_level_element.members.forEach((member, index) => {
member.jsdoc = jsdocs[index];
});
console.log(dts.emit(top_level_element)); /*=>
/**
* jsdoc for x
* /
declare const x: number;
/**
* jsdoc foy y
* /
declare const y: boolean;
*/ but notice that I haven't implemented parser for comment, so that comments in input will be ignored, only output available. |
Thanks, interesting. I'll need to look into this. :) |
I'm trying to use the // in-based
declare function path<T1 extends string, T2 extends string, TResult>(path: [T1, T2], obj: {[K1 in T1]: {[K2 in T2]: TResult}}): TResult;
declare function path<T1 extends string, T2 extends string, T3 extends string, TResult>(path: [T1, T2, T3], obj: {[K1 in T1]: {[K2 in T2]: {[K3 in T3]: TResult}}}): TResult;
declare function path<T1 extends string, T2 extends string, T3 extends string, T4 extends string, TResult>(path: [T1, T2, T3, T4], obj: {[K1 in T1]: {[K2 in T2]: {[K3 in T3]: {[K4 in T4]: TResult}}}}): TResult;
declare function path<T1 extends string, T2 extends string, T3 extends string, T4 extends string, T5 extends string, TResult>(path: [T1, T2, T3, T4, T5], obj: {[K1 in T1]: {[K2 in T2]: {[K3 in T3]: {[K4 in T4]: {[K5 in T5]: TResult}}}}}): TResult;
declare function path<T1 extends string, T2 extends string, T3 extends string, T4 extends string, T5 extends string, T6 extends string, TResult>(path: [T1, T2, T3, T4, T5, T6], obj: {[K1 in T1]: {[K2 in T2]: {[K3 in T3]: {[K4 in T4]: {[K5 in T5]: {[K6 in T6]: TResult}}}}}}): TResult;
declare function path<T1 extends string, T2 extends string, T3 extends string, T4 extends string, T5 extends string, T6 extends string, T7 extends string, TResult>(path: [T1, T2, T3, T4, T5, T6, T7], obj: {[K1 in T1]: {[K2 in T2]: {[K3 in T3]: {[K4 in T4]: {[K5 in T5]: {[K6 in T6]: {[K7 in T7]: TResult}}}}}}}): TResult;
declare function path<T1 extends string, T2 extends string, T3 extends string, T4 extends string, T5 extends string, T6 extends string, T7 extends string, T8 extends string, TResult>(path: [T1, T2, T3, T4, T5, T6, T7, T8], obj: {[K1 in T1]: {[K2 in T2]: {[K3 in T3]: {[K4 in T4]: {[K5 in T5]: {[K6 in T6]: {[K7 in T7]: {[K8 in T8]: TResult}}}}}}}}): TResult;
declare function path<T1 extends string, T2 extends string, T3 extends string, T4 extends string, T5 extends string, T6 extends string, T7 extends string, T8 extends string, T9 extends string, TResult>(path: [T1, T2, T3, T4, T5, T6, T7, T8, T9], obj: {[K1 in T1]: {[K2 in T2]: {[K3 in T3]: {[K4 in T4]: {[K5 in T5]: {[K6 in T6]: {[K7 in T7]: {[K8 in T8]: {[K9 in T9]: TResult}}}}}}}}}): TResult;
// Record-based
declare function path<K1 extends string, K2 extends string, TResult>(path: [K1, K2], obj: Record<K1,Record<K2,TResult>>): TResult;
declare function path<K1 extends string, K2 extends string, K3 extends string, TResult>(path: [K1, K2, K3], obj: Record<K1,Record<K2,Record<K3,TResult>>>): TResult;
declare function path<K1 extends string, K2 extends string, K3 extends string, K4 extends string, TResult>(path: [K1, K2, K3, K4], obj: Record<K1,Record<K2,Record<K3,Record<K4,TResult>>>>): TResult;
declare function path<K1 extends string, K2 extends string, K3 extends string, K4 extends string, K5 extends string, TResult>(path: [K1, K2, K3, K4, K5], obj: Record<K1,Record<K2,Record<K3,Record<K4,Record<K5,TResult>>>>>): TResult;
declare function path<K1 extends string, K2 extends string, K3 extends string, K4 extends string, K5 extends string, K6 extends string, TResult>(path: [K1, K2, K3, K4, K5, K6], obj: Record<K1,Record<K2,Record<K3,Record<K4,Record<K5,Record<K6,TResult>>>>>>): TResult;
declare function path<K1 extends string, K2 extends string, K3 extends string, K4 extends string, K5 extends string, K6 extends string, K7 extends string, TResult>(path: [K1, K2, K3, K4, K5, K6, K7], obj: Record<K1,Record<K2,Record<K3,Record<K4,Record<K5,Record<K6,Record<K7,TResult>>>>>>>): TResult;
declare function path<K1 extends string, K2 extends string, K3 extends string, K4 extends string, K5 extends string, K6 extends string, K7 extends string, K8 extends string, TResult>(path: [K1, K2, K3, K4, K5, K6, K7, K8], obj: Record<K1,Record<K2,Record<K3,Record<K4,Record<K5,Record<K6,Record<K7,Record<K8,TResult>>>>>>>>): TResult;
declare function path<K1 extends string, K2 extends string, K3 extends string, K4 extends string, K5 extends string, K6 extends string, K7 extends string, K8 extends string, K9 extends string, TResult>(path: [K1, K2, K3, K4, K5, K6, K7, K8, K9], obj: Record<K1,Record<K2,Record<K3,Record<K4,Record<K5,Record<K6,Record<K7,Record<K8,Record<K9,TResult>>>>>>>>>): TResult;
// for each path length list all combinations of objects and homogeneous arrays... tuples not supported yet.
declare function path<T1 extends string, TResult>(path: [T1], obj: {[K1 in T1]: TResult}): TResult;
declare function path<T1 extends number, TResult>(path: [T1], obj: TResult[]): TResult;
declare function path<T1 extends string, T2 extends string, TResult>(path: [T1, T2], obj: {[K1 in T1]: {[K2 in T2]: TResult}}): TResult;
declare function path<T1 extends string, T2 extends number, TResult>(path: [T1, T2], obj: {[K1 in T1]: TResult[]}): TResult;
declare function path<T1 extends number, T2 extends string, TResult>(path: [T1, T2], obj: {[K2 in T2]: TResult}[]): TResult;
declare function path<T1 extends number, T2 extends number, TResult>(path: [T1, T2], obj: TResult[][]): TResult;
declare function path<T1 extends string, T2 extends string, T3 extends string, TResult>(path: [T1, T2, T3], obj: {[K1 in T1]: {[K2 in T2]: {[K3 in T3]: TResult}}}): TResult;
declare function path<T1 extends string, T2 extends string, T3 extends number, TResult>(path: [T1, T2, T3], obj: {[K1 in T1]: {[K2 in T2]: TResult[]}}): TResult;
declare function path<T1 extends string, T2 extends number, T3 extends string, TResult>(path: [T1, T2, T3], obj: {[K1 in T1]: {[K3 in T3]: TResult}[]}): TResult;
declare function path<T1 extends string, T2 extends number, T3 extends number, TResult>(path: [T1, T2, T3], obj: {[K1 in T1]: TResult[][]}): TResult;
declare function path<T1 extends number, T2 extends string, T3 extends string, TResult>(path: [T1, T2, T3], obj: {[K2 in T2]: {[K3 in T3]: TResult}}[]): TResult;
declare function path<T1 extends number, T2 extends string, T3 extends number, TResult>(path: [T1, T2, T3], obj: {[K2 in T2]: TResult[]}[]): TResult;
declare function path<T1 extends number, T2 extends number, T3 extends string, TResult>(path: [T1, T2, T3], obj: {[K3 in T3]: TResult}[][]): TResult;
declare function path<T1 extends number, T2 extends number, T3 extends number, TResult>(path: [T1, T2, T3], obj: TResult[][][]): TResult;
declare function path<T>(path: any[], obj: {}): T | undefined;
declare function path(path: any[]): <T>(obj: {}) => T | undefined;
declare const a_1_b_2_c_3: { a: 1, b: 2, c: 3 };
const r = path(['a', 'b', 'c'], a_1_b_2_c_3);
// TS inferred `r` as `1 | 2 | 3`
// but it's actually `undefined`, should be inferred as something contains `undefined` The types looks perfect in theory, but TS somehow infer it weird, should I still use these types? (snapshot) |
I believe |
declare const a_1_b_2_c_3: { a: 1, b: 2, c: 3 };
declare const a_b_c: ['a', 'b', 'c'];
const r = path(a_b_c, a_1_b_2_c_3);
// still `1 | 2 | 3`
// a_b_c is inferred as [string] OK, it seems I should remove these two overloads, so that TS will choose the last overload // declare function path<T1 extends string, TResult>(path: [T1], obj: {[K1 in T1]: TResult}): TResult;
// declare function path<T1 extends number, TResult>(path: [T1], obj: TResult[]): TResult; (after) const r = path(a_b_c, a_1_b_2_c_3);
//=> {} | undefined |
I see. Ugh. Edit: to clarify, I don't see great solutions right now. Killing the overload feels a bit awkward in the sense we'd be killing like all of them to save... nothing would be left to save. If anything, a 3-length tuple matching on a 1-length one appears behavior microsoft/TypeScript#6229 aimed to address. It's a good example of why their proposal may matter. |
regressions?:
questions:
I might've accidentally repeated a few from before, feel free to ignore as appropriate. anyway, looking pretty much ready :), it's a great win even if we just merge it in right now. |
This was caused by R.gt //=> <T extends Ordered>(a: T, b: T) => boolean
// TS inferred T -> {} -> any
R.flip(R.gt) //=> CurriedFunction2<any, any, boolean>
R.until(R.flip(R.gt)(100), R.multiply(2))(1); //=> any
R.until(R.gt(R.__, 100), R.multiply(2))(1); //=> number
See ikatyang/types-ramda@5f73499#commitcomment-23207966
There is no such property in that case, should we allow that kind of things? I think it'd better to use
This needs late-inference or specify (prototype) function where<T>(
spec: Dictionary<Predicate<T>>,
object: Dictionary<T>,
): boolean; const spec = { x: R.equals(2) };
R.where(spec); //=> T = number
R.where(spec)({x: 1, y: 'moo', z: true});
// ^^^^^^^^^^^^^^^^^^^^^^^^^ [ts]
// Argument of type '{ x: number; y: string; z: boolean; }' is not assignable to parameter of type 'Dictionary<number>'.
// Property 'y' is incompatible with index signature.
// Type 'string' is not assignable to type 'number'.
Yes, (prototype) function when<T, U>(
pred: Predicate<T>,
whenTrueFn: Morphism<T, U>,
value: T,
): T | U; const truncate = R.when(
R.propSatisfies(R.flip(R.gt)(10), 'length'), //=> T = something that has 'length' property -> Record<'length', any>
R.pipe(R.take(10), R.append('…'), R.join('')) //=> U = string
);
// @dts-jest $ExpectType string -> string | Record<"length", any>
// ^ U ^ T
truncate('12345'); // => '12345'
Uh,
Not sure what you mean, currently both input and output are PromiseLike.
I think I should add some pseudo-definition for
Uh, |
Thanks for your response. So Anyway, looking forward to the PR. :) |
Final final diff with two changes (ikatyang/types-ramda#98, ikatyang/types-ramda#97), but it seems the workaround for 15768 not work (just suppress the error and output type NumberToString = { 0:'0',1:'1',2:'2',3:'3',4:'4',5:'5',6:'6',7:'7',8:'8',9:'9',10:'10',11:'11',12:'12',13:'13',14:'14',15:'15',16:'16',17:'17',18:'18',19:'19',20:'20',21:'21',22:'22',23:'23',24:'24',25:'25',26:'26',27:'27',28:'28',29:'29',30:'30',31:'31',32:'32',33:'33',34:'34',35:'35',36:'36',37:'37',38:'38',39:'39',40:'40',41:'41',42:'42',43:'43',44:'44',45:'45',46:'46',47:'47',48:'48',49:'49',50:'50',51:'51',52:'52',53:'53',54:'54',55:'55',56:'56',57:'57',58:'58',59:'59',60:'60',61:'61',62:'62',63:'63',64:'64',65:'65',66:'66',67:'67',68:'68',69:'69',70:'70',71:'71',72:'72',73:'73',74:'74',75:'75',76:'76',77:'77',78:'78',79:'79',80:'80',81:'81',82:'82',83:'83',84:'84',85:'85',86:'86',87:'87',88:'88',89:'89',90:'90',91:'91',92:'92',93:'93',94:'94',95:'95',96:'96',97:'97',98:'98',99:'99',100:'100',101:'101',102:'102',103:'103',104:'104',105:'105',106:'106',107:'107',108:'108',109:'109',110:'110',111:'111',112:'112',113:'113',114:'114',115:'115',116:'116',117:'117',118:'118',119:'119',120:'120',121:'121',122:'122',123:'123',124:'124',125:'125',126:'126',127:'127',128:'128',129:'129',130:'130',131:'131',132:'132',133:'133',134:'134',135:'135',136:'136',137:'137',138:'138',139:'139',140:'140',141:'141',142:'142',143:'143',144:'144',145:'145',146:'146',147:'147',148:'148',149:'149',150:'150',151:'151',152:'152',153:'153',154:'154',155:'155',156:'156',157:'157',158:'158',159:'159',160:'160',161:'161',162:'162',163:'163',164:'164',165:'165',166:'166',167:'167',168:'168',169:'169',170:'170',171:'171',172:'172',173:'173',174:'174',175:'175',176:'176',177:'177',178:'178',179:'179',180:'180',181:'181',182:'182',183:'183',184:'184',185:'185',186:'186',187:'187',188:'188',189:'189',190:'190',191:'191',192:'192',193:'193',194:'194',195:'195',196:'196',197:'197',198:'198',199:'199',200:'200',201:'201',202:'202',203:'203',204:'204',205:'205',206:'206',207:'207',208:'208',209:'209',210:'210',211:'211',212:'212',213:'213',214:'214',215:'215',216:'216',217:'217',218:'218',219:'219',220:'220',221:'221',222:'222',223:'223',224:'224',225:'225',226:'226',227:'227',228:'228',229:'229',230:'230',231:'231',232:'232',233:'233',234:'234',235:'235',236:'236',237:'237',238:'238',239:'239',240:'240',241:'241',242:'242',243:'243',244:'244',245:'245',246:'246',247:'247',248:'248',249:'249',250:'250',251:'251',252:'252',253:'253',254:'254',255:'255'};
declare function getValue<T extends { [index: number]: any }, N extends number>(obj: T, idx: N): T[NumberToString[N]];
const value = getValue(['a', 'b', 'c'], 0); //=> any |
Thanks for the changes. As to type-level issues, I'm still trying to figure that out, until like yesterday accidentally used older global |
Ideas
If TS cannot select the correct signature, why don't we select it ourselves?
If types are too hard to build manually, why don't we generate it programmatically?
Types
consider the following types for
R.adjust()
, it looks ugly but works perfectly in my testing.Since curried function can return itself by calling with non-parameter, we can use generic to select one of those overloads.
If we want to choose one of the overloads in the signature, it just works fine. ( the "11" means choosing the one with 2 ("11".length) parameters and both parameter are not placeholder )
ps. I think something like "11" is easily to notice which case to use, but it can be changed into something else.
and with placeholder ( the "01" means choosing the one with 2 ("01".length) parameters, the first parameter is a placeholder, and the last parameter is not a placeholder )
If there are many kinds of the function, like
R.map()
for array, functor, etc., we can create them one by one and mixed them with intersection (&
):If TS cannot find the correct signature, we can do it ourselves, it just works fine with good looking.
Implementation
To implement this types, I wrote a dts DOM library dts-element, the following code will generate the above
adjust.d.ts
.I am new to functional programming and ramda, I may not consider something else, please correct me if I am wrong.
I think functional programming is a better way to write code and hope to build a full-supported
ramda.d.ts
, let me know if you need any help, I am happy to make this types better and better.The text was updated successfully, but these errors were encountered: