Support placeholders and selectable-overloads

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.
type PH = {'@@functional/placeholder': true};
declare const __: PH;

// adjust.d.ts
type adjust = adjust_000;
type adjust_000 = {
  <T, U>(fn: (v: T) => U, index: number, array: T[]): adjust_111<T, U>;
  <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): adjust_110<T, U>;
  (_fn: PH, index: number): adjust_010;
  <X extends "111">(): <T, U>(fn: (v: T) => U, index: number, array: T[]) => adjust_111<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 "1">(): <T, U>(fn: (v: T) => U) => adjust_100<T, U>;
  <T, U>(fn: (v: T) => U): adjust_100<T, U>;
};
type adjust_001<T> = {
  <U>(fn: (v: T) => U, index: number): adjust_111<T, U>;
  (_fn: PH, index: number): adjust_011<T>;
  <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, U>(fn: (v: T) => U, array: T[]): adjust_111<T, U>;
  <T>(_fn: PH, array: T[]): adjust_011<T>;
  <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: 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>;
  (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)[];

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.
R.adjust<"11">() //=> <T, U>(fn: (v: T) => U, index: number) => adjust_110<T, U>;

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 )
R.adjust(R.__, 1)<"01">() //=> <T>(_fn: PH, array: T[]) => adjust_011<T>

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 ( & ):
type map_array = map_array_00;
type map_functor = map_functor_00;
type map = map_array & map_functor;

If TS cannot find the correct signature, we can do it ourselves, it just works fine with good looking.
(R.map as R.map_array)(blah, blah, ...)

Implementation
To implement this types, I wrote a dts DOM library dts-element, the following code will generate the above adjust.d.ts.
import * as dts from 'dts-element';

const name = 'adjust';
const placeholder = new dts.BasicType({name: 'PH'});

const generic_T = new dts.GenericType({name: 'T'});
const generic_U = new dts.GenericType({name: 'U'});

// <T, U>(fn: (v: T) => U, index: number, array: T[]) => (T | U)[]
const function_type = new dts.FunctionType({
  generics: [generic_T, generic_U],
  parameters: [
    new dts.Parameter({
      name: 'fn',
      type: new dts.FunctionType({
        parameters: [
          new dts.Parameter({name: 'v', type: generic_T}),
        ],
        return: generic_U,
      }),
    }),
    new dts.Parameter({name: 'index', type: dts.number_type}),
    new dts.Parameter({name: 'array', type: new dts.ArrayType({owned: generic_T})}),
  ],
  return: new dts.ArrayType({owned: new dts.UnionType({types: [generic_T, generic_U]})}),
});

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());

test-cases
snapshots

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.

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 (R.map as R.map_array) example is pretty interesting -- you're right that with partial application TS has trouble selecting the right variation there, and if this is a way to solve that, then that sounds like a pretty welcome alternative to having to manually override the type otherwise.
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 adjust so far.
The first thing one would notice in your version is that your definition has ended up somewhat more convoluted than the current manual version.
My interpretation here is that this difference stems from the fact that your version is also able to deal with 0-parameter application, returning the function itself. Is that right?
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. R.map<'array'>()(fn, arr), utilizing the "0-param application yields a free chance to select overload" idea you gave for currying.
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

My interpretation here is that this difference stems from the fact that your version is also able to deal with 0-parameter application, returning the function itself. Is that right?

Yes, it will return itself but with the specified overload used only.

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?).

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 adjust, it cannot pick up the overload I want to use.
example(R.adjust) //=> ???
example(R.adjust<"11">()) //=> predictable

So I started to create dts-element for generating the dts structurally.

readability for codegen vs manual
As 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
I thought that useless cases are invalid, like

0-param
R.adjust()()()()()()()()(...)

last-parameter-placeholder
R.adjust(R.__)(R.__)(R.__)(R.__)(R.__)(...)

But I found that 0-param can be used to select the specified overload, which is awesome.
And I still thought 0-param is an invalid usecase, but for selecting overload is OK.

I'm sorry, I finally understood the problem you're tackling is not 0-param calls, but Ramda placeholder (R.__), earlier requested in #120. At that time I'd also been hoping to tackle that on the level of the CurriedFunction interfaces, as you've suggested just now. For those, feel free to send a PR already, the version you showed looks good to me.
Perhaps put the placeholder versions above the normal versions though -- if T2 includes the type of PH (if it's an object or any), we'd wanna make sure it won't mistake the placeholder for a regular parameter.

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),

On second glance, I think the data structure you use for codegen isn't so difference from the one I'd added in scripts -- it looks more verbose, but I guess the idea is the same otherwise, so I guess those should be compatible. This should address the concern of verbosity for contributors to this repo.

as well as for users (code without explicit flags still works, right?).

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.

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. array as options passed through generics as well, we'd stumble on a different question though, of how these two would interact. That is, in what order might one pass these? I suppose variations like array might no longer need to be decided at the end if one is flipping argument order as well, which sounds like the answer is "either 'first', or 'figure out by case, though it'll be complicated'".

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 (&)

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 scripts.
If they can be mixed even when separate like this though, that'd be great -- makes it easier to get separate versions to pre-select by those as you suggested (be it by cast or by generic).

the above usecase is invalid, generic type can not be used as InterfaceMember

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.

Perhaps put the placeholder versions above the normal versions though if T2 includes the type of PH (if it's an object or any), we'd wanna make sure it won't mistake the placeholder for a regular parameter.

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 T2 === any since TS will select the first matched overload, you'll see the placeholder version first instead of the detailed one.
UX vs Correctness, I think correctness is better.
--

On second glance, I think the data structure you use for codegen isn't so difference from the one I'd added in scripts -- it looks more verbose, but I guess the idea is the same otherwise, so I guess those should be compatible.

My version is more verbose since I have to track the dependencies of the generic types.
for example
append: [
  ['T', 'U'],
  {
    el: 'U',
    list: 'List<T>',
  },
  '(T & U)[]'
]

It looks good, but how can I trace the dependencies of T and U ?
el ( uses U ) and list ( uses T ) can be different order, we cannot use something like returns.indexOf('T') to track it.

This should address the concern of verbosity for contributors to this repo.

I know, but UX vs Correctness again. Maybe we can link this issue as a reference?

I guess with my idea of putting variations e.g. array as options passed through generics as well, we'd stumble on a different question though, of how these two would interact. That is, in what order might one pass these? I suppose variations like array might no longer need to be decided at the end if one is flipping argument order as well, which sounds like the answer is "either 'first', or 'figure out by case, though it'll be complicated'".

Sorry, I just forgot this case.
Since I can generated it manually, so I think it can be generate by codegen, I'll try it latter.
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[]): U[];
  <T>(_fn: PH, array: T[]): map_01_array<T>;
  <X extends "11">(): <T, U>(fn: (v: T) => U, array: T[]) => 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: T[]): Mappable<U>;
  <T>(_fn: PH, mappable: Mappable<T>): map_01_mappable<T>;
  <X extends "11">(): <T, U>(fn: (v: T) => U, mappable: T[]) => 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[]): U[];
  <T, U>(fn: (v: T) => U, mappable: T[]): 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[]) => 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 'mappable', X extends "11">(): <T, U>(fn: (v: T) => U, mappable: T[]) => 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;
  <K extends 'array'>(): map_00_array;
  <T, U>(fn: (v: T) => U): map_10_mappable<T, U>;
  <T, U>(fn: (v: T) => U): map_10_array<T, U>;
};

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 scripts. If they can be mixed even when separate like this though, that'd be great -- makes it easier to get separate versions to pre-select by those as you suggested (be it by cast or by generic).

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

CurriedFunction

Did you mean the following code?

Yeah, both CurriedFunction and the actual typings, yeah. Like, in this part the PH version should go first too in case of any / whatever:
  <T, U>(fn: (v: T) => U, index: number, array: T[]): adjust_111<T, U>;
  <T, U>(fn: (v: T) => U, _index: PH, array: T[]): adjust_101<T, U>;

how can I trace the dependencies of T and U?

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.

It seems it just mixin the first layer

Hm, yeah, looks like merging typings using just & wouldn't get what we'd need for TS. That said, we're currently already able to generate both mixed and separate versions (mixed versions are generated when there are multiple options, i.e. for add, separate versions is what it generates when there are no additional options, like for append), so I guess the hardest bits there have been tackled already...
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).

i.e. for add

Isn't it and ?!

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).

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)[];

interaces
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 .. ?

Isn't it and ?!

Both add and and have multiple, yeah. Just like map you used in your example just now. :)

I'd suggest making some scripts for copy-paste automatically

Yeah, fair enough.

How can I do for this repo now?

If you've already generated the CurriedFunction interfaces, feel free to update them here.
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...

Both add and and have multiple, yeah. Just like map you used in your example just now. :)

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',
  ]
},

If you've already generated the CurriedFunction interfaces, feel free to update them here.
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...

I thought that just updating the CurriedFunction interfaces does not make sense..
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 :)

I thought this is not for add...

Ouch, crap, thanks for noticing! Fixed now.

I thought that just updating the CurriedFunction interfaces does not make sense..

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.

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 :)

Fair enough, sorry for the trouble!
P.S.: Saw you're from Taiwan; pretty cool place. :D

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.

Would you mind the CurriedFunction 1~9 to be 1000+ rows...

P.S.: Saw you're from Taiwan; pretty cool place. :D

😊

Would you mind the CurriedFunction 1~9 to be 1000+ rows...

On that one, well...
I used to have similar codegen for the path function, trying to give it proper inference, which also ended up super long for 7~9. When I tried it, I could no longer compile my application anymore -- the path definition made it too slow. So then I just got rid of the longer segments.
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
Curried2: 3 rows
Curried3: 7 rows
Curried4: 15 rows
Curried5: 31 rows
Curried6: 62 rows
Curried7: 127 rows
Curried8: 255 rows
Curried9: 511 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 curry and invoker), while in my experience a big number of overloads could significantly affect compilation times.
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 andable parameters in the source file of Ramda.
Additionaly, Ramda v0.24.1 had just released.
Here's my progress now, any suggestion?

Whoops, seems you're right the dispatching for R.and was no longer there anymore. I ditched that now. Feel free to point out / sent PRs for others!
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 dts-jest positioned?
That said, the typings-checker fork used now has had its motivations as well (see there + #123) --  preventing any inferences, and a version-controlled diff log until tests pass. In practice, this means string-based exact-match testing for types, and using code snippets over line numbers in the error output.
@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):

Make typings code base and tests more consistent and well structured
Ensure all functions are typed and tests
Allow to use/import ramda functions individually

Also covered:

comments
code-gen in TS over JS
don't rely on JS objects for order

@ikatyang:

support placeholders (R.__)
selectable overloads (for placeholders, typing variants like array/object in R.map)

Others I'm making out from the code (could you comment on these?)

sorting typings
abstracting out parameter names
comments, incl. JSDoc

@tycho01:

reduce per-typing code for maintainability and reduce learning curve for contributors
generating mixed interface for curried variants, needed cuz TS ignores interface below the first that matches
automatic deference of generic declaration to the first time they're required. complication: if only used by another generic (e.g. with keyof) instead of in the type signature itself (curried away to later invocation), inference fails.
consideration for performance, in my path() experience shown to drop with dozens of 100+ overloads

Some comments I had on @whitecolor's approach:

the desire to also split output into separate files convoluted the code-gen to handle imports; I proposed addressing this by keeping source separate, output together.

enable import merge from 'ramda/src/merge' by flipping it around and having those reference the big file, instead of the big file reference the smaller files?

Some questions on yours:

How does the sorting match TS's order from specific to general, minus placeholders as you mentioned?
abstracting out param names and structures into function calls (e.g. ${_.List('T')})): both seems a bit more verbose this way than the original strings; were these to facilitate mass renaming? On param names, I guess Ramda has these in their codebase, even if not mentioned in docs, but yeah, I'm not so worried about names.

Here's my progress now, any suggestion?

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 path before.
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...

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.

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 R.and() above. I agree with adding real tests.

Could you perhaps comment a bit on how you see dts-jest positioned? That said, the typings-checker fork used now has had its motivations as well (see there + #123) -- preventing any inferences, and a version-controlled diff log until tests pass. In practice, this means string-based exact-match testing for types, and using code snippets over line numbers in the error output.

The reason I created dts-jest is that it is more comfortable for users to test, since jest has its powerful CLI interaction and watch mode, see Jest: Immersive Watch Mode.
In dts-jest, you can use :show flag at first to see what type is it, and so as to choose snapshot it or not, see also dts-jest testing, that said if any appeared, you'll notice that might be a wrong type.
For the version-controlled diff log, you can use :show flag unless the type is correct, or manually adjust the snapshot so as to see the difference between them.
And yes, there is also code snippets with line numbers in the output feature in dts-jest.
Additionally, I think the testing in this repo now is somehow weird, I can't understand why tests/test.ts: 450 / 725 checks passed. will pass the test in travis, isn't that pass = all correct ?

Others I'm making out from the code (could you comment on these?)

sorting typings
abstracting out parameter names
comments, incl. JSDoc

sorting typings
The sorting function is here, currently I just sort with the following rules:

placeholder version go first
normal typings go fisrt
selectable typings will be inserted before the last typings

abstracting out parameter names
I'm not sure what name to use so I choose to make them referenced so as to refactor easily, and I want to have the consistent naming with checking for length of generic.

comments, incl. JSDoc
I'm a big fan for using Go to Definition to see the detail information, so that with JSDoc I can get the fully infomrations, and I used to see somewhere that VSCode will support more kind of JSDoc tag in the future.

How does the sorting match TS's order from specific to general, minus placeholders as you mentioned?

I think TS is smart to choose correct typings, and if it can't, just use the selectable overload.

abstracting out param names and structures into function calls (e.g. ${_.List('T')})): both seems a bit more verbose this way than the original strings; were these to facilitate mass renaming? On param names, I guess Ramda has these in their codebase, even if not mentioned in docs, but yeah, I'm not so worried about names.

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.

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.

I'll try it later, thanks for pointing out.

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 path before.

I have no idea what problem you had faced to path before, but I think my version seems flat(?) enough to reference between each others. And of course, if it will affect performance, I think we can just make some version like normal, with placeholder, with selectable-overloads, fully-typed or something else, they can just select which to use basd on their RAM(?).

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...

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 path, basically, I had hundreds of overloads as well, and tests worked fine, but actually compiling a mobile project using it suddenly didn't really work anymore until I cut out on most of the overloads there. That one was also flat, at least. But yeah, if it turns out to have a bad impact then offering options in different branches or the like seems a fair solution there.
I also eagerly await the horizon of the TS roadmap. :)

On overload order w.r.t. placeholders, I'm under the impression we may still have a problem

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

add real tests
use standard naming instead of abstract naming
migrate those exist typings in this repo
check those exist typings with Ramda source code
support Ramda v0.24.x
and something I haven't realized?

Let me know if this is correct or not :)

add real tests

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.

use standard naming instead of abstract naming
migrate those exist typings in this repo

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.
It'd just allow switching from say my codegen to your codegen, and support e.g. normal, with placeholder, with selectable-overloads, fully-typed modes, etc., with 0 per-function chores.
I think that aspect is pretty powerful of not putting functions into the data. I guess control kinda gets inverted.

check those exist typings with Ramda source code

Well, I suppose if we have run-time tests, then ones for like R.and dispatching would probably error fast, notifying us of the changes. I think they've mostly been cutting down on dispatching anyway, so in that sense things have gotten less complicated. It looks like placeholders have been spared so far though! 😄

support Ramda v0.24.x

Ideally, though leaving it to its own separate issue is fine as well.

and something I haven't realized?

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

use standard naming instead of abstract naming

See templates/map.d.ts -> src/map.d.ts and templates/README.md for example.

migrate those exist typings in this repo
check those exist typings with Ramda source code

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 Lens.

support Ramda v0.24.x

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 R.isArrayLike.
Not Yet

add real tests
multi-version typings

I haven't done this yet, but it seems not hard to do.

DefinitelyTyped compatible

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.

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.

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.

The .d.ts templates are looking fairly manageable as well. I suppose it might be possible to further expand on the current idea by allowing overload selection based on just either selector or kind, but obviously you're already ahead in this respect anyway.
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 eq typing with type-level type checks so we could just directly use Ramda's own tests to verify the typings as well.
Unfortunately there are multiple obstacles there:

type-level type checks aren't possible yet (TS's 6606)
we'd need further power to do value-level prediction (TS's 5453)
currying currently requires more testing for the typings (for implicit overload selection and late info for e.g. keyof)
verifying equality of function types (might work by default?)
verifying intended presence of type errors (can't use tsc anyway, could maybe ditch them)
the same values could be passed to the functions in TS using different type representations, which can and will affect inference quality

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 .c.ts files though, the targeted representation is slightly sub-optimal now compared to what they could look like if we were instead generating a POJO (/AST?) representation there. For the purpose of directly generating the TS representation, that seems to get a bunch of cruft from the TS representation with things like .join(',').
I guess conversion from such an abstracted representation could be centralized -- that way the .c.ts cruft could just be handled in one place instead of everywhere separately. Note this is still compatible with the option to just directly use .d.ts typings like in your current distinction.
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 enabling value-level prediction in TS to overcome 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. ☺

I suppose it might be possible to further expand on the current idea by allowing overload selection based on just either selector or kind, but obviously you're already ahead in this respect anyway.

I used to think about selectable-overloads with just $SEL or $KIND, it is possible but it will cause more and more overload types and bad consistency, for example:
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, $SEL is always the first type param, $KIND is the second type param which might be optional based on their conflict between kinds.

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?

The root cause why my integration test ramda-tests.ts is all-in-one file, is that TS had to parse a lot of ramda/src/*.d.ts declarations before type-checking, so that separate file will cause bad performance on dev time ( 4~6 sec per test file on my PC, and as you can see my unit test time on Travis CI, it was terrible ).
And you can see the difference affects the types before/after the PR in the snapshot files, e.g. ikatyang/types-ramda@91c817a.

Honestly I love how you lifted Ramda's JSDoc comments to get top-level support, and I wish we could similarly just provide an eq typing with type-level type checks so we could just directly use Ramda's own tests to verify the typings as well.

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.

Unfortunately there are multiple obstacles there:

type-level type checks aren't possible yet (TS's 6606)

The typeof for expression is not supported yet, but the programmatic-level typeof for expression is supported, that's how our typings-checker and dts-jest works.

we'd need further power to do value-level prediction (TS's 5453)

Let's wait for that, I think it is the most important feature for FP.

currying currently requires more testing for the typings (for implicit overload selection and late info for e.g. keyof)

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 keyof vs Record, but it is impossible to fix something like the above example unless setting the type parameter explicitly.

verifying equality of function types (might work by default?)

Is that you mean the difference between function expression and function declaration? If so, they are the same thing on the type-level.

verifying intended presence of type errors (can't use tsc anyway, could maybe ditch them)

Is that you mean the failing tests? Currently I just set them as :fail to ensure they throw errors, and for others that expected passed but failed, I just set them as :skip, and you can set them as :show to see their type.

the same values could be passed to the functions in TS using different type representations, which can and will affect inference quality

Yes, but I can't figure out what case will be, so I think this kind of issue should be resolved after someone reported.

I think for the purpose of the .c.ts files though, the targeted representation is slightly sub-optimal now compared to what they could look like if we were instead generating a POJO (/AST?) representation there. For the purpose of directly generating the TS representation, that seems to get a bunch of cruft from the TS representation with things like .join(',').

It seems that the representation like your script.js is more reasonable, I'll improve those *.c.ts later.

I guess conversion from such an abstracted representation could be centralized -- that way the .c.ts cruft could just be handled in one place instead of everywhere separately. Note this is still compatible with the option to just directly use .d.ts typings like in your current distinction.

I think we can place their repeated helper function to ./utils, and then still write same format but more readable code there? Just like your script.js.

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 enabling value-level prediction in TS to overcome the need for codegen to make TS usable than about the specific representations used...

Yes, it is. But it seems FP is not on their 1st level priority.

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. ☺

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: master for dev, generated-full-typed, generated-normal, etc. for installation.
And yes, using Ramda to coding is always my goal, I'll switch to Ramda once I confirm that it won't cause problems.

If 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.

Yes, but I think TS's emitted declaration file will be terrible if someone want to export curried function directly...

it will be a lot of breaking changes for placeholder support. Could you elaborate on that?

I'm not sure about this, but I think it might be, since DefinitelyTyped and @types are separated for a long time, thus the order of overloads probably different, and I noticed that someone add some additional types to DT, e.g. CurriedTypeGuard, which is not exist in our repo.

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 Maybe, I dunno.

I used to write types for Maybe, but it is somehow hard to do, since there are so many mixin and fully es5-based, that is, a lot of prototype assignment. I'll try it again later.

Summary

improve *.c.ts
use Ramda for codegen if possible
write ramda-fantasy types for testing

It seems very close to our goal now. :)

breaking changes

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.

TS's emitted declaration file will be terrible if someone want to export curried function directly...

What would be your take on that?

R.sort(R.__, people)(byAge); //=> T === Person

I think most of these cases are caused by typings using keyof, and could be solved by falling back to typings not using that for cases where this problem kicks in. I think fallback typings we've already had for most of those cases.
Detecting the conditions (keyof used in the declaration of a generic needed for a provided param, with its target being a generic depending on info not yet available) and dropping the failing typing, to ensure it'll fall back to a working version, is one thing I think we can do as part of the codegen (see here.
That said, I think this covers cases such as R.path, for which a potential nicer alternative might not require keyof (see here). For many other cases it may need 5453 though.

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?

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.

TS's emitted declaration file will be terrible if someone want to export curried function directly... What would be your take on that?

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.

R.sort(R.__, people)(byAge); //=> T === Person
I think most of these cases are caused by typings using keyof, and could be solved by falling back to typings not using that for cases where this problem kicks in. I think fallback typings we've already had for most of those cases.
Detecting the conditions (keyof used in the declaration of a generic needed for a provided param, with its target being a generic depending on info not yet available) and dropping the failing typing, to ensure it'll fall back to a working version, is one thing I think we can do as part of the codegen (see here.
That said, I think this covers cases such as R.path, for which a potential nicer alternative might not require keyof (see here). For many other cases it may need 5453 though.

filter those unusable types, but it seems works fine even not removed, just have to ensure keyof goes first and then Record, and yes, we have to handle those more complex cases.

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];

generic that used more than once: make generics as late as possible (suppose the latest is the most specific).

-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

I'm not good at those type-level operation, so I may just pick up those types you mentioned. ( R.path only? )

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.

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 $ExpectType, I have no idea what it want to check for.
I haven't read those issues yet, so I may have to read them and make those test suites be in my tests.

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.

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. :)

I'm not good at those type-level operation, so I may just pick up those types you mentioned. ( R.path only? )

Yeah, I'd gladly help out on anything in that area. Probably not many others who have been looking into that much anyway, haha.
It's still kinda in WIP though; I dunno why that path version fails when used as a function yet, and currently we're limited to arrays for input (only thing we can iterate over, think I can hack up object iteration with 5453) and anything except constructed arrays for output (can't manipulate those yet until 5453), making R.path one of the rare few functions (path, pathOr, mergeAll, fromPairs, zipObject) fitting the criteria of what we might be able to do now. So a bunch more is imaginable but we still have a few roadblocks.

There is the old test, I just copy-paste and normalize/lint them so as to match my pattern.

Awesome; saw tests for map differed and figured they might not be in yet. Might you have an output log for that so we could compare?

Additionally, I removed those issue-cases due to their incompleteness, some of them even does not have $ExpectType, I have no idea what it want to check for.

The most time consuming part is testing

Compilation part or run-time part?
Like, if we're only changing typings, then I imagine we could get by not re-running the JS tests, since they'd be guaranteed unchanged. In that sense I guess time for the JS ones isn't much of a concern, especially since it doesn't imply longer compilation times for users.

Awesome; saw tests for map differed and figured they might not be in yet. Might you have an output log for that so we could compare?

I can't figure out what you mean about map, since I completely migrate them, and just remove those duplicate cases ( there are so many duplicates.. ) and those weird cases ( commented // /* */, no $ExpectTypeed, etc. ), that's it, I haven't remove other cases.
And I fixed those error types by using selectable-overloads, and then mark them as :pass (snapshoted), their inferred type match the original $ExpectType value, what I mean match is that they are equivalent but may represent in difference form, e.g. Dictionary<number> vs Record<string, number> ).
@types/npm-ramda's map test
ikatyang/types-ramda's map test

Compilation part or run-time part?

It's runtime part (TS parses those generated .d.ts files so as to infer types).

Like, if we're only changing typings, then I imagine we could get by not re-running the JS tests, since they'd be guaranteed unchanged. In that sense I guess time for the JS ones isn't much of a concern, especially since it doesn't imply longer compilation times for users.

I always execute my build-watch script in the background (almost immediately compiled, you can try my npm run build-watch script and then modify templates/*.d.ts to see the performance), and so does the jest test (watching mode, npm run test -- --watch), so that jest will start testing changed files once it found its test files changed.
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.

match the original $ExpectType value, what I mean match is that they are equivalent but may represent in difference form, e.g. Dictionary<number> vs Record<string, number>).

Does that mean it checks type A > B / B < A? I ask because the string-based checks (using :show?) you mentioned earlier seem one of few ways to prevent any inference from type-checking.

And I fixed those error types by using selectable-overloads, and then mark them as :pass (snapshoted)

I take it imperfect inference would be :pass-muted now, but do we know if there were any regressions? I imagine the only way to find out would be to compare error output for the same test suite?

Does that mean it checks type A > B / B < A? I ask because the string-based checks (using :show?) you mentioned earlier seem one of few ways to prevent any inference from type-checking.

I checked their inferrence by using :show at the first time, so that its inferred type will console.log on the jest CLI so I can confirm that it is correct, and then I'll set that flag to :pass, which mean not only no errors, but also "snapshot" its inferred type in snap file, jest will throw error once the value has changed. (some screenshots)
For checking both A -> B and B -> A, I have checked all B -> A, but some of A -> B are not valid since I make some types more specific, for example:
A (Dictionary<number>) -> B (Record<'some-literal', number>)

I take it imperfect inference would be :pass-muted now, but do we know if there were any regressions? I imagine the only way to find out would be to compare error output for the same test suite?

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 --updateSnapshot.
before

inferred types are placed in ./ramda-tests.ts
expressions are placed in ./ramda-tests.ts

after

inferred types are placed in ./__snapshots__/ramda-tests.ts.snap
expressions are placed in ./ramda-tests.ts

Yeah, that's clear, though it makes the comparison before/after this PR somewhat harder since the formats no longer match. Could we run the old test suite the old way once so we could diff the output and ensure any potential inference changes have been for the better?

Yeah, that's clear, though it makes the comparison before/after this PR somewhat harder since the formats no longer match.

I think I'll add some post-test script to generate some diff-friendly files so as to better compare.

Could we run the old test suite the old way once so we could diff the output and ensure any potential inference changes have been for the better?

Yes, but I think I'll do it at last, since it's not interesting for me to migrate those tests thrice (I have already migrated them twice...)

Looking forward to it. :)

I'm working on lazy inference for generics (aka late inference), see the dts diff and inference diff (start from L6981 if no auto-jump) for current dts implementation and inference results, as you can see, it is too strict for those literal types (1, 'str', etc.), otherwise it is great, I have no idea how to improve them, or should I just mark them as expected?
Some example:
// prototype
function clamp<T extends Ordered>(min: T, max: T, value: T): T;

// lazy inference
function clamp<
  T$1 extends Ordered,
  T$2 extends T$1 = T$1,
  T$3 extends T$2 = T$2
>(min: T$1, max: T$2, value: T$3): T$3;

// ● clamp › R.clamp(1, 10, -1)
//     Argument of type '10' is not assignable to parameter of type '1'.

-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

What the. Even if you put in 5 over -1, it'll still fail right?
That said I think type-level ordinal operators (> / <) could be viable (notes). With that we may be able to accuratele type this clamp.
The sort one is kinda interesting... I'm surprised the original fails.
Out of curiosity, given all the pain you're going through to add them, why do you like placeholders? I've used R.flip a few times but found that to suffice usually. :P

Even if you put in 5 over -1, it'll still fail right?
The sort one is kinda interesting... I'm surprised the original fails.

Yes, since the lazy inference supposes the later is more specific then the former, for example:
type Ordered = number | string | boolean | Date;

// prototype
declare function $clamp<T extends Ordered>(min: T, max: T, value: T): T;

// lazy inference
declare function clamp<
  T$1 extends Ordered,
  T$2 extends T$1 = T$1,
  T$3 extends T$2 = T$2
>(min: T$1, max: T$2, value: T$3): T$3;

declare const number: number;

clamp(1, 10, -5);
//       ^^ 10 extends 1 is not allowed
clamp(number, 10, -5);
//                ^^ -5 extends 10 is not allowed
clamp(number, number, -5); //=> -5, passed but should be number actually
clamp(number, number, number); //=> number, passed as expected

$clamp(1, 10, -5); //=> 1 | 10 | -5
$clamp(number, 10, -5); //=> number
$clamp(number, number, -5); //=> number
$clamp(number, number, number); //=> number

interface PH { placeholder: true; }
interface Person { name: string; age: number; }
declare function byAge(something: {age: number}): number;
declare const people: Person[];

// prototype
declare function $sort<T>(fn: (v: T) => number, list: T[]): T[];

// lazy inference
declare function sort<T$1, T$2 extends T$1 = T$1>(fn: (v: T$1) => number, list: T$2[]): T$2[];

sort(byAge, people); //=> Person[]
// lazy inference ensure that the latest is the most specific

$sort(byAge, people); //=> {age: number}[]
//    ^^^^^ TS will just pick the first matched generic if they shared the generic

Out of curiosity, given all the pain you're going through to add them, why do you like placeholders? I've used R.flip a few times but found that to suffice usually.

Ah, I just want to have a fully-typed Ramda.
R.flip can only flip the first two parameter, so that it can't cover all the cases, though there seems just a few more-than-2-param functions, and yes, placeholders are more readable!
For now, it is not only for the placeholder support, there are more features that we can have, like the lazy-inference, though I may have to disable it on some cases, haha.

If even the obvious clamp(1, 10, 5) // -> should be 5 errors, you'd wonder if it's adding much value over the crappy number typing. :P
As to placeholders, I guess technically you could go round more than once (even for a 2-param function you could technically used the placeholder 2 or more times). But yeah, legitimate use-cases for that elude me so far.
I'd like to properly type partial.lenses, but for the time being it's fairly hopeless haha.

https://github.com/ramda/ramda/issues/2177#issuecomment-302696168
Not sure you'd appreciate this 😅

I have no idea about that, since I haven't tried that way before, haha.

I just released my v0.24.1 yesterday, I tried to show the diff between this repo and my repo, but there are too many conflicts, e.g. TS version (especially 2.4.1), deprecated/outdated types (R.isArrayLike, lens.set, etc.).
I'm already done the diff-friendly file, but I'm still thinking about how to show the difference between repos, maybe I should remove those deprecated-related types (It seems not just a little) and test your definitions my way, then switch to my definitions and test again to show their difference?

About late-inference, I tried to implement it but failed since my implementation was too strict, see diff. I'd appreciate if there are something like (non-)literal-assertion for generics in TS, since it's hard to implement late-inference without it...

maybe I should remove those deprecated-related types (It seems not just a little)

If we have a diff I don't mind manually checking over it and just ignore the deprecated ones.

test your definitions my way, then switch to my definitions and test again to show their difference?

I guess as long as we're testing the same thing that'd work. That said, you've tested for similarity, while I'd tested for conformance. I guess technically with the latter it'd help us filter out the changes that suddenly made things pass. I'd definitely just go through it manually though, so in that sense either way should work here.

About late-inference, I tried to implement it but failed since my implementation was too strict, see diff.

Yeah, seems for clamp/concat/insert/reduce/reduceRight/reduceWhile/until their run-time results no longer match the type. It now also gives generics even when unused (complicates it without adding value). Maybe we could have tests confirm if the run-time result's type is a sub-type of the inferred type to automatically catch these...

I'd appreciate if there are something like (non-)literal-assertion for generics in TS, since it's hard to implement late-inference without it...

What do you mean by (non-)literal-assertion? And isn't what you've currently got already late inference? That is to say, types become known at the moment the params get passed, right?

const a1 = 1; //=> 1
let a2 = 1; //=> number

let b1 = {n: 1}; //=> {n: number}
const b2 = {n: 1}; // want {n: 1}, but still same as above

b2 should be doable as well if I could get support for my proposal on that in https://github.com/Microsoft/TypeScript/issues/16389. That said it's still hard to control - if you pass values without saving them as variables it seems you get granular types. But from a user perspective, it should result in usable types either way...
We can technically do your 1# using 1 as number or <number> 1, but yeah...

maybe we should blacklist/whitelist some functions to get the best user experience?

It'd be cool if it could check itself automatically whether the generated type would pass the test, then decide based on that. But yeah. I wish I could already propose dependent types for these functions already, but we can't operate on functions yet, so anything like R.reduce is too early still...
For concat specifically though, I imagine the result type could just be Array<(1 | 2 | 3) | (4 | 5 | 6)> though? I actually managed to do that one on the type level too now (though outputting ArrayLike objects over actual tuples), but guess it won't help much while I can't properly use these types in functions yet, see https://github.com/Microsoft/TypeScript/issues/17086...

Here's the pure diff, I haven't changed any test case, so that there are some deprecated/outdated functions and some mismatch type parameters.
NOTE: late-inference is disabled, since it is currently not good enough.

Great :), looks like quite a bit got better!
Still seeing some potential regressions as well though:

a bunch of new Argument of type A is not assignable to parameter of type B for expressions that apparently worked fine in JS
some Expected 0-A arguments, but got B.
Type A does not satisfy the constraint B.
Property A does not exist on type B.
types giving any now: until, forEachObjIndexed
type degeneration ({}): path / pathOr / mergeWith / props / lensIndex / indexBy / intersectionWith
other potential regressions:

pipeP? (is that R3 bound to number?)
truncate
filter: until we can do better, that Partial was probably appropriate
the xLens bits returning lenses after being used
pickBy / pickAll
splitAt: binary tuple seemed accurate?
R.and(why, false)
the filterIndexed bits give Dictionary for the arrays -- I thought that wouldn't match since Dictionary uses a string index?

I noticed you had List copy the ArrayLike definition. I guess that'll look shorter in types. I thought the T[] | I'd added back there once someone was having trouble without it though. Forgot which thread though, and still find it weird.
It seems StringRepresentable is gone as well. I guess that one was a case of prescriptive vs. descriptive typing: I'd taken the descriptive route there, where JS would accept anything as a string due to the implicit toString conversion. I suppose forcing users to make explicit casts there would bring some of the rigor of more strongly-typed languages, so your route there seems pretty fair.
There were a few other cases of descriptive typing in there, including that List (which allowed e.g. Immutable.js Lists for 'array' input but notably also strings), as well as Struct, which helped express that most places that in the first place expected objects for input would also settle for things with numerical indices -- e.g. array-likes. I see currently you mostly left those unconstrained (in e.g. assoc). I guess that works too. :)

a bunch of new Argument of type A is not assignable to parameter of type B for expressions that apparently worked fine in JS

For R.unless, it's caused by removed-functions like R.isArrayLike, see R.unless - coerceArray
For those R.append-like list functions, I'm just allowd same type T now, it seems I should also allowed widen-case T | U.

some Expected 0-A arguments, but got B.

For R.invoker, its type is outdated and so does the test case, see Ramda REPL and source code.
// @dts-jest $ExpectType string -> Expected 0-2 arguments, but got 3.
R.invoker(0, 'toUpperCase', 'foo');
// @dts-jest $ExpectType string -> Expected 0-2 arguments, but got 4.
R.invoker(1, 'charAt', 'foo', 1);

Type A does not satisfy the constraint B.

Uh.., it's caused by the type parameter
declare function tryCatch<F extends Function>(tryer: F, catcher: F): F;

const x = R.prop('x');
// @dts-jest $ExpectType boolean -> Type 'boolean' does not satisfy the constraint 'Function'.
R.tryCatch<boolean>(R.prop('x'), R.F)({x: true}); // => true
// @dts-jest $ExpectType boolean -> Type 'boolean' does not satisfy the constraint 'Function'.
R.tryCatch<boolean>(R.prop('x'), R.F)(null);      // => false

It works fine without type parameter.
// tryCatch<(<T extends Record<"x", any>>(object: T) => T["x"])>(
//   tryer: <T extends Record<"x", any>>(object: T) => T["x"],
//   catcher: <T extends Record<"x", any>>(object: T) => T["x"]
// ): <T extends Record<"x", any>>(object: T) => T["x"] (+5 overloads)

R.tryCatchR.prop('x'), R.F)({x: true}); //=> true

Property A does not exist on type B.

It seems just those removed/outdated types only, e.g. R.isArrayLike, R.isNaN, lens(), lens.set(), just ignore them.

types giving any now: until, forEachObjIndexed

For R.until, it's caused by no late-inference
R.until //=> <T>(pred: Predicate<T>, fn: Morphism<T, T>, initial: T): T;
R.gt //=> <T extends Ordered>(a: T, b: T): boolean

R.flip(R.gt) //=> CurriedFunction2<any, any, boolean>
//                                 ^^^  ^^^

const a = R.flip(R.gt)(100) //=> CurriedFunction1<any, boolean>
//                                                ^^^

const b = R.multiply(2) //=> CurriedFunction1<number, number>

R.until(a, b) //=> CurriedFunction1<any, any>
//     ^^^^^^ no late-inferece, T === any from `a`

So does the R.forEachObjIndexed:
let printKeyConcatValue = (value: any, key: string, obj: any) => console.log(key + ':' + value);
//                                ^^^
// this is caused by no late-inference too

// @dts-jest $ExpectType {x: 1, y: 2}
R.forEachObjIndexed(printKeyConcatValue, {x: 1, y: 2});
// @dts-jest $ExpectType {x: 1, y: 2}
R.forEachObjIndexed(printKeyConcatValue)({x: 1, y: 2});
// @dts-jest $ExpectType [1, 2]
R.forEachObjIndexed(printKeyConcatValue, [1, 2]);
// @dts-jest $ExpectType [1, 2]
R.forEachObjIndexed(printKeyConcatValue)([1, 2]);

type degeneration ({}): path / pathOr / mergeWith / props / lensIndex / indexBy / intersectionWith

I'll check them again.

pipeP? (is that R3 bound to number?)

R.pipeP(
    (m: number) => Promise.resolve(R.multiply(2, m)),
    (m: number) => Promise.resolve(m / 2),
    R.multiply(2) //=> number -> number
)(10);

I'm just allowd PromiseLike as return value now, seems I should also allow non-PromiseLike, since they'll be promisified automatically.
PS: It seems there will be a promised type operator in TS 2.5 (https://github.com/Microsoft/TypeScript/pull/17077), would be better to wait for that?

truncate

I'll check it again, it's somehow weird. 😅

filter: until we can do better, that Partial was probably appropriate

agree.

the xLens bits returning lenses after being used

It'll just work fine if you write this way:
before
// let xLens = R.lens<number, xy>(R.prop('x'), R.assoc('x'));
let xLens = R.lens<number>(R.prop('x'))(R.assoc('x'));
// ^ works with only 1 generic, for curried version managed to split the inferred generic from the manual generic
// @dts-jest $ExpectType number -> <T>(lens: Lens<T, { x: number; y: number; }>) => T
R.view(xLens, {x: 1, y: 2});            // => 1
// @dts-jest $ExpectType { x: number, y: number } -> (lens: Lens<number, { x: number; y: number; }>) => { x: number; y: number; }
R.set(xLens, 4, {x: 1, y: 2});          // => {x: 4, y: 2}
// @dts-jest $ExpectType { x: number, y: number } -> {}
R.set(xLens)(4, {x: 1, y: 2});          // => {x: 4, y: 2}
// @dts-jest $ExpectType { x: number, y: number } -> Argument of type '{ x: number; y: number; }' is not assignable to parameter of type 'Lens<number, {}>'.
R.set(xLens, 4)({x: 1, y: 2});          // => {x: 4, y: 2}
// @dts-jest $ExpectType { x: number, y: number } -> (lens: Lens<number, { x: number; y: number; }>) => { x: number; y: number; }
R.over(xLens, R.negate, {x: 1, y: 2});  // => {x: -1, y: 2}
// @dts-jest $ExpectType { x: number, y: number } -> Argument of type '{ x: number; y: number; }' is not assignable to parameter of type 'Lens<number, {}>'.
R.over(xLens, R.negate)({x: 1, y: 2});  // => {x: -1, y: 2}
// @dts-jest $ExpectType { x: number, y: number } -> {}
R.over(xLens)(R.negate, {x: 1, y: 2});  // => {x: -1, y: 2}

after
interface XY {
  x: number;
  y: number;
}
const xLens = R.lens<XY['x'], XY>(R.prop('x'))(R.assoc('x'));

// @dts-jest:pass -> number
R.view(xLens, {x: 1, y: 2}); //=> 1
// @dts-jest:pass -> { x: number; y: number; }
R.set(xLens, 4, {x: 1, y: 2}); //=> {x: 4, y: 2}
// @dts-jest:pass -> XY
R.set(xLens)(4, {x: 1, y: 2}); //=> {x: 4, y: 2}
// @dts-jest:pass -> XY
R.set(xLens, 4)({x: 1, y: 2}); //=> {x: 4, y: 2}
// @dts-jest:pass -> { x: number; y: number; }
R.over(xLens, R.negate, {x: 1, y: 2}); //=> {x: -1, y: 2}
// @dts-jest:pass -> XY
R.over(xLens, R.negate)({x: 1, y: 2}); //=> {x: -1, y: 2}
// @dts-jest:pass -> XY
R.over(xLens)(R.negate, {x: 1, y: 2}); //=> {x: -1, y: 2}

pickBy / pickAll

It seems Partial is more appropriate, I'll improve that.

splitAt: binary tuple seemed accurate?

I thought I wrote tuple there, but it seems not, I'll add them back. 😅

R.and(why, false)

It's a outdated type, there is no and-able (why) now.

the filterIndexed bits give Dictionary for the arrays -- I thought that wouldn't match since Dictionary uses a string index?

It's caused by R.addIndex + R.filter (array/filterable/dictionary version), if there is no additional type parameter for R.filter, it'll just take the last (most general) one -- Dictionary version.
There is no way to retain its overloads, TS will just choose one, unless you use something like <F extends Function>(x: F): F, but R.addIndex needs its overloads to choose appropriate overload, since R.addIndex can push index and obj/list to arguments, just like Array#reduce and Array#map, its arguments' count can be 3, 4, etc.

I noticed you had List copy the ArrayLike definition. I guess that'll look shorter in types. I thought the T[] | I'd added back there once someone was having trouble without it though. Forgot which thread though, and still find it weird.

There is the T[] in my List, not sure what you mean.
export type List<T> = T[] | ArrayLike<T>;

It seems StringRepresentable is gone as well. I guess that one was a case of prescriptive vs. descriptive typing: I'd taken the descriptive route there, where JS would accept anything as a string due to the implicit toString conversion. I suppose forcing users to make explicit casts there would bring some of the rigor of more strongly-typed languages, so your route there seems pretty fair.

I removed it since StringRepresentable is just like {}, #toString() is always inherit from Object, which is the base prototype in javascript.
It does nothing for type-checking, except null and undefined.

There were a few other cases of descriptive typing in there, including that List (which allowed e.g. Immutable.js Lists for 'array' input but notably also strings), as well as Struct, which helped express that most places that in the first place expected objects for input would also settle for things with numerical indices -- e.g. array-likes. I see currently you mostly left those unconstrained (in e.g. assoc). I guess that works too. :)

I'll add Struct back, and so does those unconstrained generics.

it'll just take the last (most general) one -- Dictionary version.

This one might not be general enough there since it specifies a different index. I guess this was one of the cases I'd use that Struct in when I needed something general like that.

There is the T[] in my List, not sure what you mean.

Okay, my bad.

toString() is always inherit from Object

Fair enough. Forgot why I'd added it anyway.
Looking fairly ready for a PR by now :), though I guess commit logs might not have overlap. I guess it'd be possible to just copy files over, but you might 'lose' your commits so far that way. Open to ideas!

Promises: just add that when it's ready I guess?

This one might not be general enough there since it specifies a different index. I guess this was one of the cases I'd use that Struct in when I needed something general like that.

Agree, should use Struct for the general case.

Looking fairly ready for a PR by now :), though I guess commit logs might not have overlap. I guess it'd be possible to just copy files over, but you might 'lose' your commits so far that way. Open to ideas!

I don't mind losing those commits, a big PR is enough.
My main issue is how to handle those chores, e.g.

coding style

I use my own tslint-config-ikatyang, it seems inappropriate for a community project

deploy

need a GH_TOKEN set on Travis for multi-version
or just PR to DefinitelyTyped (I'd prefer this)

changelog

I think having a changelog is great

README.md

I have no idea how to merge them

And for late-inference, what should I do for it? It currently just disabled.

I use my own tslint-config-ikatyang, it seems inappropriate for a community project

Sounds like a minor issue to me; guess it can still be changed at any time anyway.

deploy
need a GH_TOKEN set on Travis for multi-version

Node versions? I think multiple version support for those shouldn't be a concern since our typings don't affect the run-time, right?

or just PR to DefinitelyTyped (I'd prefer this)

I presume we'd push to both? DT is great to distribute to users while a separate repo like this helps for issue discussion I guess.

changelog

Yeah, sure. I've just been lazy so far.

README.md

If you've got a PR in I can give feedback :), but not too worried about it.

Late inference: if it's still giving some issues (the strict ones), otherwise maybe first just merge what you already have?

... I hope I haven't overlooked more of your questions, otherwise feel free to remind me.

Node versions? I think multiple version support for those shouldn't be a concern since our typings don't affect the run-time, right?

Uh.., I mean the simple/placeholder/selectable-overloads/full-feature

I presume we'd push to both? DT is great to distribute to users while a separate repo like this helps for issue discussion I guess.

👍

Sounds like a minor issue to me; guess it can still be changed at any time anyway.

I'd like trying to use prettier+tslint recently, and I prefer using snake_case for not annoying something like getHTMLString/getHtmlString, haha. It's not common usage for most people. 😅

Late inference: if it's still giving some issues (the strict ones), otherwise maybe first just merge what you already have?

Just like I mentioned above, if there is no something like whitelist/blacklist for it, it'll be too strict for some case. So I think we'll just disabled the late-inference, and pick it up in the future?

I hope I haven't overlooked more of your questions

Me too, this thread is somehow too long. 😅

I'd like trying to use prettier+tslint recently, and I prefer using snake_case for not annoying something like getHTMLString/getHtmlString, haha. It's not common usage for most people. 😅

Yeah, seems camelcase may be more common in JS (and notably Ramda), but I get where you're coming from there. Overall your repo seems a good step forward either way, so these stylistic preferences seem insignificant by comparison really.

I mean the simple/placeholder/selectable-overloads/full-feature

Uhh, I presume if you make some PR on branch C it'd run tests on branch C, no?

So I think we'll just disabled the late-inference, and pick it up in the future?

Yeah, that was what I was thinking as well.

Uhh, I presume if you make some PR on branch C it'd run tests on branch C, no?

Yes, and if that PR merged in the master branch with travis passed, it'll trigger the deploy script automatically, so that the dist-* branch will always be the newest one.

Okay, I see. Sounds good. So what would be the problem if we're missing out on the multiple version support?

Uh, it's just for people who may not want to use the full-feature version somehow? Single version is OK to me.

How's that? You can still use branches even if travis only tests the one you're submitting edits for right?
Well, sounds fine to me either way. :)

Every branch will be tested (expect dist-*), but only the master branch will trigger the deploy script.
And it'll always test the full-feature one, other version won't be tested.

Oh, yeah, people that pull from DT can't really get our other branches. Not much we can do there I guess.

types-ramda v0.24.2 milestone
npm-ramda-snapshot: types-ramda#v0.24.1...v0.24.2
npm-ramda-snapshot: npm-ramda...types-ramda#v0.24.2

And also there are still some error due to the different order of type-parameters, you might have to see snapshots/ramda-tests.ts to figure out the appropriate usage.
Let me know if this looks good to you, then I'll make a PR as soon as possible. :)
NOTE: The R.path-like functions are still returning any since its type-level operation is still blocked.

the new regressions may still be low-hanging fruit to improve; I haven't gone through the other diff (is that with types/npm-ramda?), but I should probably go through the full thing again once those are down.

The diff you reviewed is my last change (0.24.2 milestone), the full diff between this repo and my v0.24.2 is npm-ramda-snapshot: npm-ramda...types-ramda#v0.24.2.

The new regression seems most caused by type parameter mismatching, things I have to improve:

filter: need 6606
dissoc: remove keyof constraint
something I haven't noticed (?)

And I realize that it seems the 6606 can solve the literal-widen issue like this:
declare function widen(value: boolean): boolean;
declare function widen(value: number): number;
declare function widen(value: string): string;
declare function widen<T extends [any]>(value: T): T[number][];
declare function widen<T>(value: T): T;

// need 6606
type Widen<T> = typeof widen(T);

declare function clamp<T extends Ordered>(
  min: Widen<T>,
  max: Widen<T>,
  value: Widen<T>,
): Widen<T>;

but it seems still have to wait for a long time, Accepting PRs + Community means core team won't work on it.. 😢

Sorry, I finally realize what's happened to 17086...
The placeholder version dissoc was blocked by it.
R.dissoc(R.__, {a: 1, b: 2, c: 3})('a') //=> Pick<{ a: 1; b: 2; c: 3; }, never>

And for the IntersectionUnions you said, I don't know how to use it for dissoc since I'm not familiar with type-level operation...

Oh, if your string version already worked curried, then problem solved.
If you are running into 17086 though, I fear I don't know of a solution yet. :/

I just test it with non-constraint version, but its result looks weird...
(prototype)
export function $key<V extends {}, K extends string>(property: K, object: V): Omit<V, K>;
export function $general<T extends {}>(property: Property, object: T): T;

(generated)
// only paste those used function
declare const dissoc: dissoc_00;
type dissoc_00 = {
    <V extends {}>(_property: PH, object: V): dissoc_key_01<V>;
};
type dissoc_key_01<V extends {}> = {
    <K extends string>(property: K): dissoc_key_11<V, K>;
};
type dissoc_key_11<V extends {}, K extends string> = Omit<V, K>;
export = dissoc;

R.dissoc(R.__, {a: 1, b: 2, c: 3})('a') //=> Pick<{ a: 1; b: 2; c: 3; }, "a" | "b" | "c">

I have no idea how this happened, the first curry received <K extends string>(property: K) => Pick<{a: 1, b: 2 c: 3}, "a" | "b" | "c">, I'm sure this is the $key version since it's K not Property, how weird...

<K extends string>(property: K) => Pick<{a: 1, b: 2 c: 3}, "a" | "b" | "c">? So it's like, that "a" | "b" | "c" is injected before the K is even known? That sounds really weird. That'd definitely explain why you'd still get that after the last argument has been supplied, but... I'll admit that doesn't exactly explain how they could have been disconnected.

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 promised thread as something you'd considered waiting for, but considering the limited overlap that can't be it right?

I'm currently use Promise<T> | T as a workaround for promised, so the pipeP-like functions are fine now.
The only thing I concerned is that the R.path type, it's blocked by the type Something = SomeOperation[NumberLiteral], so that it currently uses any as return type, which will be a regression in this repo if I send a PR and merged.

That isn't https://github.com/Microsoft/TypeScript/issues/15768 right?
If it is a regression, might it not be an option to use the earlier one for that?

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 dts-element-fp first, and then making the PR here.

Fair enough 👍
On Jul 31, 2017 2:33 PM, "Ika"<EMAIL_ADDRESS>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
https://github.com/types/npm-ramda/issues/173#issuecomment-318980589,
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 typical as well, though I guess Ramda typings are just that too.
If JSDoc really looks at more than just the comments, that binding is what you're doing here huh?

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 path types in this repo, but I found that there are some unexpected behavior:
// 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?

I believe ['a', 'b', 'c'] may be regarded as to be of type string[] or [string, string, string] or something. Could you try putting that on an explicitly typed variable as well like the other one?
I've been discussing solutions to this (not degenerating types unless necessary) in #16389 and #16656.

(playground)
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 T | undefined instead of the weird [string].
// 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.
So even if you reorder the overloads like 3-2-1 instead, the 3 (trinary?) overload fail as it's missing the nested objects.
Since length 3 tuples somehow match length 1 tuples that 1 overload ends up matching, as that much of the ins succeed.
T must then become "a" | "b" | "c" to match the actual contents of the tuple though, at which point the property access operation yields 1 | 2 | 3.
Ugh. Honestly overload approaches are pretty awkward when you have to deal with different circumstances. path overloads were exploding way too hard already. Hope we could just get that.

Final diff and changes, looks like there is no regression now (some of them require late-inference to get accurate type, but I think it's currently acceptable), once you confirmed I'll start to work on a PR.

regressions?:

until
into: "Argument of type ..."
prop / propOr "not assignable to ..."
where error

questions:

truncate: is the Record<"length", any> what your late-inference comment refers to?
mapIndexed regression: how come it could do without the late inference before?
pipeP: if we're outputting PromiseLike, maybe use for input too?
lenses: a few errors, guess we need to sync this up with their current workings (also just came up in masaeedu's issue), I had a quick glance at it but couldn't quite make it out yet
pathOr: yields value 2 but gives type {} | "N/A" -- might be problematic since afaik 2 as a primitive doesn't match {}. I'll agree it'd be unfortunate to give up on having something more granular than the any though. I'm still working on getting type-level crap to better work out, hope I could help out there within too long.

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.

until

This was caused by R.flip and R.gt, use placeholder version can fix this problem.
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

into: "Argument of type ..."

See https://github.com/ikatyang/types-ramda/commit/5f734995b86d2a59f57057a04f2268668ecc1a11#commitcomment-23207966

prop / propOr "not assignable to ..."

There is no such property in that case, should we allow that kind of things? I think it'd better to use as any for this kind of cases.

where error

This needs late-inference or specify T to be any.
(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'.

truncate: is the Record<"length", any> what your late-inference comment refers to?

Yes, T is considered Record<'length', any> after R.propSatisfies, need late-inference to let T = string after truncate('12345').
(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'

mapIndexed regression: how come it could do without the late inference before?

Uh, mapIndexed was removed in ramda 0.16.0, so there's no mapIndexed in my types, it just uses R.addIndex now, which require more information (late-inference or manual type parameter) to get accurate type.

pipeP: if we're outputting PromiseLike, maybe use for input too?

Not sure what you mean, currently both input and output are PromiseLike.

lenses: a few errors, guess we need to sync this up with their current workings (also just came up in masaeedu's issue), I had a quick glance at it but couldn't quite make it out yet

I think I should add some pseudo-definition for lensProp and lensIndex to get accurate type, currently I'm just use kind of ManualLen to define every lens, which is usually useless (always inferred T to be {}).

pathOr: yields value 2 but gives type {} | "N/A" -- might be problematic since afaik 2 as a primitive doesn't match {}.

Uh, number is assignable to {}, {} means everything except null and undefined, I think what you mean "doesnt't match" is for object type.

Thanks for your response.
So where is potentially too strict now, for restricting the parameters to homogeneous objects.
I realize we can't really do a proper heterogeneous check yet, and a compromise would make the stricter homogeneous version useless.
Until we can do better though, I guess it's better to be a bit less strict about it if that's what it takes to cut out the false positive though.
Anyway, looking forward to the PR. :)

Final final diff with two changes (https://github.com/ikatyang/types-ramda/pull/98, https://github.com/ikatyang/types-ramda/issues/97), but it seems the workaround for 15768 not work (just suppress the error and output any)
(playground)
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 tsc for typical tests; still got some issues left. Your work here looks pretty much ready though.