Giter Club home page Giter Club logo

hotscript's People

Contributors

adamsuskin avatar aero8 avatar beraliv avatar ecyrbe avatar eloytoro avatar frankie303 avatar ggrandi avatar gvergnaud avatar jessekelly881 avatar sno2 avatar un4uthorized avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

hotscript's Issues

Objects.Omit and Objects.Pick make all properties required

In version 1.0.11, Objects.Omit and Objects.Pick make all properties required. It also ignores readonly and make everything writable

https://codesandbox.io/s/typescript-playground-export-forked-5y6rtd

import { Pipe, Objects, Identity } from "hotscript";

type HasNoOptionals1 = Pipe<
//   ^?
  {
    readonly a: string;
    b: number;
    c?: boolean;
    d?: number;
  },
  [Objects.Omit<"a">]
>;

type HasNoOptionals2 = Pipe<
//   ^?
  {
    readonly a: string;
    b: number;
    c?: boolean;
    d?: number;
  },
  [Objects.Omit<"">]
>;

type HasNoOptionals3 = Pipe<
//   ^?
  {
    readonly a: string;
    b: number;
    c?: boolean;
    d?: number;
  },
  [Objects.Pick<"a" | "c">]
>;

type HasNoOptionals4 = Pipe<
//   ^?
  {
    readonly a: string;
    b: number;
    c?: boolean;
    d?: number;
  },
  [Objects.Pick<"a" | "b" | "c" | "d">]
>;

type WithIdentity = Pipe<
//   ^?
  {
    readonly a: string;
    b: number;
    c?: boolean;
    d?: number;
  },
  [Identity]
>;
Output
export {};
Compiler Options
{
  "compilerOptions": {
    "strict": true,
    "noImplicitAny": true,
    "strictNullChecks": true,
    "strictFunctionTypes": true,
    "strictPropertyInitialization": true,
    "strictBindCallApply": true,
    "noImplicitThis": true,
    "noImplicitReturns": true,
    "alwaysStrict": true,
    "esModuleInterop": true,
    "declaration": true,
    "target": "ES2017",
    "jsx": "react",
    "module": "ESNext",
    "moduleResolution": "node"
  }
}

Playground Link: Provided

`Negate<0>` returns `number | bigint`

Hi!

I noticed that Negate<0> returns number | bigint and I'd expect it to return 0 (doesn't seem like TypeScript has -0, which is fine by me).

import { Call, Numbers } from 'hotscript'

type ShouldBeZero = Call<Numbers.Negate<0>>
//   ^?

type Zero = -0;
//   ^?

https://www.typescriptlang.org/play?#code/JYWwDg9gTgLgBAbzgYQIYBt0Bo4DkCuIARgKZQDOcAvnAGZQQhwDkAFhDOQMZTBgzMAUIJgBPMCTgBldvnQATAEIkAWmQhwAvCgzoAPAWJlyAOlwkA5qhgk9ABgB8DwQHoXcDwD0A-MLES4NQYtOABaOwBuV3cvbyA

Allow changing object property specifier (?) when updating it

The feature request is to make this possible:

$<Objects.update<'path', 1>, {path?:2}> // $ExpectType {path: 1}

That would be a breaking change, and doesn't support going the other direction. So probably a new function like this:

$<Objects.update<'path', 1>, Objects.updateSpecifier<'-?', 'path'>, {path?:2}> // $ExpectType {path: 1}

Example in Tuples.GroupBy is incorrect

Document says

interface IsNumber extends Fn {
  return: this["arg0"] extends number ? true : false;
}

type T0 = Call<Tuples.GroupBy<IsNumber>, [1, "str", 2]>;
//   ^? { true: [1, 2], false: ["str"] }
type T2 = Call<Tuples.GroupBy<Strings.StartsWith<"a">>, ["alice", "bob", "carl"]>;
//   ^? { true: ["alice"], false: ["bob", "carl"] }

But it's actually

interface IsNumber extends Fn {
  return: this['arg0'] extends number ? true : false;
}

type T0 = Call<Tuples.GroupBy<IsNumber>, [1, 'str', 2]>;
// never
type T2 = Call<Tuples.GroupBy<Strings.StartsWith<'a'>>, ['alice', 'bob', 'carl']>;
// never

Because boolean can not be the property name

`Objects.AllPaths` typed as `never` with dictionaries

type MyDictionary = {
  [key: string]: {
    name: string;
    age: number;
  };
};

export type Paths = Call<Objects.AllPaths, MyDictionary>; // never

Expected behaviour IMO would be string | "${string}.name" | "${string}.age". Is this possible?

[Bug report]: `Unions.Range` end value

Re #41

I could be mistaken, but I don't think Unions.Range includes the final element. In other words, Unions.Range<1, 3> would match 1 and 2, but not 3.

Including the final element by default would be more intuitive in my opinion, and would only require a small adjustment to the function (something like | EndValue).

Partial property

There is a function Objects.Partial but it turns all properties on an object to optional. Would be nice to have a version that targets a specific property: Objects.SetPropertyOptional<'foo', SomeObject>.

[Feature Request] Add support for `Simplify` type function to reduce (expand) types

I think it would help to add a Simplify type function to reduce(expand) types, like this:

Definition

type Flatten<T> = T extends Function ? T : T extends object ? { [K in keyof T]: Flatten<T[K]> } : T;
export type Simplify<T> = Flatten<T> & {};

Usage

interface SomeData {
  a: number;
  b?: boolean;
}
type SomeMoreData = SomeData & {
  c: string;
}

type WithoutSimplify = SomeMoreData;           // -> SomeData & { c: string; }
type WithSimplify    = Simplify<SomeMoreData>; // -> { a: number; b?: boolean; c: string; }

Support for Enumerate and Range

Support for fractional numbers with Numbers types

Currently, trying to use a non-integer will only perform the operation on the integer portion of the number:

import { Apply, Numbers } from "hotscript";

type DoubleHalf = Apply<Numbers.Mul, [0.5, 2]>; // evaluates to 0
type DoubleTwoPointFive = Apply<Numbers.Mul, [2.5, 2]>; // evaluates to 4

Playground link

Couldn't find any mention of it on this repo, so just was wondering if this is unintentional, planned to be implemented, or not being added?

Building with hotscript as dependency

Hey there! Thanks for the repo, it's very nice, just as your course (passed it and liked a lot!).

We at Evil Martians have plans to extend our nanostores ecosystem with a form library called nanoform. And for it we wanted to make a map store that will provide typesafe object path interface for the underlying structure. That's where we use Objects.Get and Objects.AllPaths utilities.

It works perfectly from the DX point of view, but we have an issue when building the lib. It's probably not hotscript specific, but I hoped you could give any guidance on how to fix that.

Here's a repro on StackBlitz. You need to run npm run build in terminal there. See the problem is that tsc inlines a lot of hotscript code in the .d.ts file making it unreadable mess.

image

What's extremely odd is that it does that for one function, getPath, but not the other, setPath, even though both use the same set of utility types. Do you have any idea how to get rid of this? Thanks!

[Feature Request]: `Not`, `NotIn`, and `NoIntersect` handlers for validation and duplicate checks

There are several goals I am hoping to achieve here, with these type helpers:

  • Ensure a type is not another type

    const a: string & Not<"test"> = "test";
    //    ^ TS should complain
  • Ensure a type is not in another grouped type like an array or object

    Array example

    const forbiddenStrings = ['bad', 'worse', 'worst'] as const;
    const a: string & NotIn<typeof forbiddenStrings> = "bad";
    //    ^ TS should complain

    Object keys example

    const forbiddenStrings = { bad: 1, worse: 2, worst: 3 } as const;
    const a: string & NotIn<keyof typeof forbiddenStrings> = "bad";
    //    ^ TS should complain
  • Compare objects of different types to ensure none contain the same keys or elements (in the case of arrays)

    const wordsA: string[] & NoIntersect<typeof wordsB, keyof typeof wordsC> = ['a', 'b', 'c'] as const;
    //    ^ TS should complain
    const wordsB: string[] & NoIntersect<typeof wordsA, keyof typeof wordsC> = ['d', 'e', 'f'] as const;
    //    ^ TS should complain
    const wordsC: string[] & NoIntersect<typeof wordsA, typeof wordsB> = { g: 1, h: 2, i: 3 } as const;
    //    ^ TS should complain

I'm' not sure what the API for this would look like, likely not what I showed above but something more argument-based like this…

type SomeType = NoIntersect<typeof wordsB, keyof typeof wordsC>;

…if the base type can be inferred, otherwise passed explicitly as the first arg like this:

type SomeType = NoIntersect<string[], typeof wordsB, keyof typeof wordsC>;

Objects.PathTypeMap feature request

Something like this would be useful to be able to get the type at a given path, if possible.

/*
 * { "a": { b: number }, "a.b": number  }
 */
type Ret = Objects.PathTypeMap<{ a: { b: number } }>

Add integer range `IntRange` function

It could be useful to match a number between a set of number, which a range function could be useful for.

Definition

type Enumerate<N extends number, Acc extends number[] = []> = Acc['length'] extends N
  ? Acc[number]
  : Enumerate<N, [...Acc, Acc['length']]>;

type IntRange<F extends number, T extends number> = Exclude<Enumerate<Simplify<T>>, Enumerate<F>> | T;

Usage

type CustomRange = IntRange<20, 300>; // -> 20 | 21 | ... many more ... | 300

Disclaimer & credit

This type is inspired (99% copied) from this StackOverflow answer by AlexG, with a slight modification to include the last number in the series (| T). Without this change, IntRange<20, 300> would match 20299 instead or 20300.

Not work when Call argument is generic parameter

Code:

import { Call, Fn } from 'hotscript';

interface Just extends Fn {
  return: this['arg0'];
}

function fn<T extends string>(a: T, b: Call<Just, T>) {
  type JustString = Call<Just, String>;

  const aLength = a.length;
  const bLength = b.length;

  const js: JustString = '';
  const jsLength = js.length;
}

Error:

Property 'length' does not exist on type '(Equal<T, unique symbol> extends true ? [] : [T]) extends [infer arg, ...any[]] ? arg : never'.

11   const bLength = b.length;
                       ~~~~~~

[Proposal] New encoding for Functions

Motivation

Our current function encoding creates a dichotomy between arguments received through Apply (this["args"]) and arguments passed explicitly via type parameters. This means every function needs to manually merge these two types of inputs, which is cumbersome and error prone.

In addition, we have a weird distinction between our "pipeable functions" that can't be called directly but only through Call, Apply or Eval, and regular generic types that can be called directly, but aren't "pipeable".

Proposed Solution

Here is what I think could be a good solution:

  • Fn extensions always receive their argument through Apply, and read them using a new Fn.args<this> function.
  • Wrap our Fn extensions in type aliases that support partial inputs by using a new Curry function.
  • (mostly unrelated to this problem) rename output into return, to stay close to the semantics of a value-level function.

Pros:

  • We can partially apply a function or give it all of its arguments using the same api: MyFn<A, B, C>
  • We no longer have to manually merge our different types of arguments
  • Fn.args<this> is easier to type than this["args"]
  • Fn.arg0<this> is easier to type than this["args"][0]
/**
 * Core
 */
export interface Fn {
  args: unknown
  return: unknown;
}

export type Apply<fn extends Fn, args extends unknown[]> = (fn & { args: args })["return"];

namespace Fn {
  export type args<F> =
    F extends { args: infer args extends unknown[] } ? RemoveUnknownArrayConstraint<args> : never;

  export type arg0<F> =
    F extends { args: [infer arg, ...any] } ? arg : never;

  export type arg1<F> =
    F extends { args: [any, infer arg, ...any] } ? arg : never

  export type arg2<F> =
    F extends { args: [any, any, infer arg, ...any] } ? arg : never

  export type arg3<F> =
    F extends { args: [any, any, any, infer arg, ...any] } ? arg : never
}


export type Pipe<acc, xs extends Fn[]> = xs extends [
  infer first extends Fn,
  ...infer rest extends Fn[]
]
  ? Pipe<Apply<first, [acc]>, rest>
  : acc;

export type placeholder = "@hotscript/placeholder";
export type unset = "@hotscript/unset";



export interface CurryImpl<fn extends Fn, partialArgs extends unknown[]>
  extends Fn {
  return: MergeArgs<Fn.args<this>, partialArgs> extends infer args extends unknown[]
    ? Apply<fn, args>
    : never
}

type Curry<fn extends Fn, partialArgs extends unknown[]> =
  unset extends partialArgs[number]
  ? CurryImpl<fn, partialArgs>
  : placeholder extends partialArgs[number]
  ? CurryImpl<fn, partialArgs>
  : Apply<fn, partialArgs>

/**
 * Custom function
 */

/**
 * Parameters are assigned with Fn.args, Fn.arg0, Fn.arg1, Fn.arg2, etc.
 */
interface ExcludeNumberFn extends Fn {
  return: Fn.arg0<this> extends infer T ? T extends number ? never : T : never
  //                                      👆
  //                     Since we assigned T, this becomes
  //                     A naked type variable, which means
  //                     This expression distributes.
  //
  //                     It wouldn't with the simpler: 
  //                     `Fn.arg0<this> extends number ? never : Fn.arg0<this>`.
}

/**
 * Wrap this in a call with curry:
 */
type ExcludeNumber<T = unset> = Curry<ExcludeNumberFn, [T]>

/**
 * We can use it directly:
 */
type exclude1 = ExcludeNumber<"Gabriel" | 1 | 2 | "hello">
//    ^? "Gabriel" | "hello"

/**
 * Or through Apply!
 */
type exclude2 = Apply<ExcludeNumber, ["Gabriel" | 1 | 2 | "hello"]>
//    ^? "Gabriel" | "hello"


/**
 * It becomes more interesting with several parameters:
 */
interface PrependFn extends Fn {
  // you don't have to merge piped args with partial args manually:
  return: Fn.args<this> extends [infer Sep extends string, infer Str extends string]
    ? `${Sep}${Str}`
    : never
}

// Because this is done by `Curry`
type Prepend<Sep = unset, Str = unset> = Curry<PrependFn, [Sep, Str]>

// You can partially apply and pipe:
type re3 = Pipe<"Hello", [
  //  ^? "start_start_start_Hello"
  Prepend<"start_">,
  Prepend<"start_">,
  Prepend<"start_">,
]>

// You can use it directly:
type res2 = Prepend<"start", "end">
//    ^? "startend"

// Here is what you get if you partially applied:
type res4 = Prepend<"start">
//    ^? CurryImpl<PrependFn, ["start", "@hotscript/unset"]>

// And here what you get when you "pipe":
type res5 = Apply<Prepend<"start">, ["end"]>
//    ^? "startend"


/**
 * With this new encoding, we can also get rid of some weirdness 
 * We have with function of arity 2 like `Extends` when partially applied.
 */

// This is what you would expect:
export interface ExtendsFn extends Fn {
  return: [Fn.arg0<this>] extends [Fn.arg1<this>] ? true : false
}

// and we handle the swapping of arguments here instead: 
type Extends<A = unset, B = unset> =
  Curry<ExtendsFn, B extends unset ? [unset, A] : [A, B]>
  //                                       👆
  //                              Swap if B is unset

type res6 = Extends<2, number>
//   ^? true

type res7 = Pipe<"hello", [Extends<string>]>
  //   ^? true
  // The arguments are swapped here

👉 Playground

Other explored solutions

Use a function type to assign parameters and get the output

I also tried this approach of using a function type on Fn to both assign arguments and compute the output:

export interface Fn {
  args: unknown
  fn(): unknown;
}

export type Apply<fn extends Fn, args extends unknown[]> =
    ReturnType<(fn & { args: args })["fn"]>;
//      👆
//   We need to get the return type

export interface ExtendsImpl extends Fn {
  fn<A extends Fn.arg0<this>, B extends Fn.arg1<this>>():
  // 👆 args are declared like this
    [A] extends [B] ? true : false
  // 👆 and this is the body of our function
}

// This stays the same
type Extends<A = unset, B = unset> =
  Curry<ExtendsImpl, B extends unset ? [unset, A] : [A, B]>

type res6 = Extends<2, number>
//   ^? true

type res7 = Pipe<"hello", [Extends<string>]>
  //   ^? true

pros:

  • The definition of arguments and return types of our function looks familiar
    cons:
  • It means TS will run ReturnType<...> a lot, which could be bad for perf

👉 playground

question: inherit return type of type property

Hello, is it possible to inherit the return type dynamically of a given property? For example, let's assume we have a React component:

type InputProps = {
  value: string
}

export const Input: FC<InputProps> = () => (<></>);

And then, we have the following type:

type FormField = {
  component: Component | ForwardRefExoticComponent<any> | any;
  props: ??? // it should get the return 
 }

const fields: FormField[] = [
  {
      component: Input,
      props: { value: "test" }
  }
]

I've been trying this from the past few hours and couldn't achieve any valuable results.

Best.

Proposal for Zip

Proposal for the base function

We maybe can setup Fn to be generic of input types. like so :

interface Fn<T=unknown> {
  input: T;
  output: unknown;
}

This allows to implement specialised algorithms (for arrays, strings, numbers) without having to type cast, exemple for pipe:

interface PipeFn extends Fn<{
    acc: unknown;
    item: Fn;
  }> {
  output: Call<this["input"]["item"], this["input"]["acc"]>
}

Making these actually easier to read

Tuples has no exported member Create

It seems the the readme is currently out of date on this and that T.Create is no longer part of the public API, at least as far as i can see. Is this correct?

Numbers

The tuple trick to do number operations is really limited.

I have a complete implementation here with no limitations but ts memory and compute power :
https://github.com/ecyrbe/ts-calc

But it would be really out of scope, maybe ?

Sharing anyway just in case.

BUG: does not work properly with tsconfig module, moduleResolution, target

version: 1.0.11
typescript: 5.0.4

package.json

{
  "name": "hot",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "type": "module",
  "scripts": {},
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "@types/node": "^18.16.0",
    "hotscript": "^1.0.11",
    "typescript": "^5.0.4"
  }
}

does not work

tsconfig.json

{
  "compilerOptions": {
    "module": "ESNext",
    "moduleResolution": "nodenext",
    "target": "ESNext",
    "declaration": true,
    "esModuleInterop": true,
    "strict": true,
    "outDir": "dist/"
  },
  "include": ["src/"]
}

image

does work

tsconfig.json

{
  "compilerOptions": {
    "declaration": true,
    "esModuleInterop": true,
    "strict": true,
    "outDir": "dist/"
  },
  "include": ["src/"]
}

image

issue

hotscript does not appear to work with these settings

...
    "module": "ESNext",
    "moduleResolution": "nodenext",
    "target": "ESNext",
...

does anybody have any ideas?
just wanted to use hotscript in my code but this "bug" currently stops me from using it.

Working with function types

Examples:

  • Remove a union member from the return type
  • add a union member to the return type
  • remove a paramter
  • add a paramter
  • map over the paramters
  • ...

Would this be in scope for this project?

[Feature Request] Support for `Digit` and `Digits`

It would be helpful to be able to specify Digits and Digit types.

Definitions

import * as H from 'hotscript'

type Digits<T extends number> = H.Eval<H.Unions.Range<
  H.Eval<H.N.Power<10, H.Eval<H.N.Sub<T, 1>>>>,
  H.Eval<H.N.Sub<H.Eval<H.N.Power<10, T>>, 1>>
>> | (T extends 1 ? 0 : never);

type Digit = Digits<1>;

Usages

const some1DigitNumber: Digit = 5; // ✅
const some1DigitNumberString: `${Digit}` = "5"; // ✅
const some2DigitNumber: Digits<2> = 99; // ✅
const some2DigitNumberString: `${Digits<2>}` = "99"; // ✅

TypeScript Playground: https://www.typescriptlang.org/play?#code/JYWwDg9gTgLgBAKjgQwM5wBJwGZQiOAcgAsIZUBjKYMGQg…


UPDATE: I've refined the example to include the recently supported Unions.Range.

Piping String to Object fails due to Zip; "Type instantiation is excessively deep"

Screenshot 2023-03-03 at 23 41 05

Attemnpting to make a route-function similar to the example shown in the readme, but simpler. Above code causes ts-error. Type instantiation is excessively deep and possibly infinite. ts(2589). Toggling Tuples.Zip seems to trigger the error. In above example zip is utilised to transform ["user", "friend"] to [["user"], ["friend"]] and then mapping the latter to [["user", string], ["friend", string]] and then ultimately to the final object. Thoughts?

TS-playground repro.

dump() or toString() functions

I've looked for some time, but didn't find a solution yet: is it possible to add some functions to get an AST of a type, or at least a text representation? At least basic ones... My idea is to be able to have a representation of a type in runtime, similar to JsonSchema but replaced inline. And if there's some way, can it be added to hotscript library?

Add installation instructions to README

Providing installation instructions allows users to understand how to install the package without thinking and it's always nice to get a break from thinking for a sec!

`Objects.Update` | Incorrect type if value is replaced by array

Reproduction:
type T1 = Call<Objects.Update<'a[0]', 4>, { a: 1 }>;

Output:

type T1 = {
    a: {
        toString: ((radix?: number | undefined) => string) & (() => string);
        toFixed: (fractionDigits?: number | undefined) => string;
        toExponential: (fractionDigits?: number | undefined) => string;
        toPrecision: (precision?: number | undefined) => string;
        valueOf: (() => number) & (() => Object);
        toLocaleString: {
            ...;
        } & (() => string);
        0: number;
    };
}

Expected:

type T1 = { a: number[] }

Unions.ToTuple not working with Match

Hi, is this a bug?

type Bug<U extends "a" | "b" | "" = ""> = Call< Tuples.Map<Match<[
  Match.With<"a", { a: true }>,
  Match.With<"b", { b: true }>,
  Match.With<any, Identity>,
]>>, Unions.ToTuple<U>>

type test1 = Bug<"a"> // every arg yields never

// but this half works

type HalfWorks<U extends "a" | "b" | "" = ""> = Call< Tuples.Map<Match<[
  Match.With<"a", { a: true }>,
  Match.With<"b", { b: true }>,
  Match.With<any, Identity>,
]>>, [U]>

type test2 = HalfWorks<"a"> // [{ a: true; }]
type test3 = HalfWorks<"a" | "b"> // ["a" | "b"]

Did I miss anything?

Edit:
I realize there's a workaround:

Call< Unions.Map<Match<[
  Match.With<"a", { a: true }>,
  Match.With<"b", { b: true }>,
  Match.With<any, Identity>,
]>>, U>

However, I'm curious why the above bug example doesn't work.

Support regex patterns in `Strings.Replace`

Wouldn't it be nice if we could write things like Strings.Replace<'([A-Z]_?[a-z])|([a-z]_?[A-Z])', '&1-&2'>?

If somebody is up for a type challenge, here is a playground: Playground 😘

import {Pipe, S, T, Compose} from 'hotscript'

export type CamelCase<Str> = Pipe<
  Str,
  [
    S.Replace<"/([a-z])([A-Z])/g", "$1-$2">,
    S.Replace<"_", "-">,
    S.Split<"-">,
    T.Map<Compose<[S.Capitalize, S.Lowercase]>>,
    T.Join<"">,
    S.Uncapitalize
  ]
>;

type res1 = CamelCase<"ONE_two-three">;
//   ^?
type test1 = Expect<Equal<res1, "oneTwoThree">>;
type res2 = CamelCase<"one_TWO-three">;
//   ^?
type test2 = Expect<Equal<res2, "oneTwoThree">>;
type res3 = CamelCase<"one_two-THREE">;
//   ^?
type test3 = Expect<Equal<res3, "oneTwoThree">>;
type res4 = CamelCase<"ONE_TWO-THREE">;
//   ^?
type test4 = Expect<Equal<res4, "oneTwoThree">>;
type res5 = CamelCase<"alreadyInCamelCase">;
//   ^?
type test5 = Expect<Equal<res5, "alreadyInCamelCase">>;

[Feature Request] Object.set

Hi, thank you for the library 👍
How about we add Object.Set which is similar to Object.Update. But you can update and set new paths.

Call<Objects.Set<'a.b', 2>, { a: 1 }>;
// { a: { b: 2 }}

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.