TypeScript对象的部分应用

huangapple go评论45阅读模式
英文:

Partial application of TypeScript object

问题

我想要部分应用一个对象到一个接受单个对象作为参数的函数中。我已经能够使下面的`partial`函数返回一个表现如预期的函数,但我不能让partial函数拒绝提供的`x`对象中多余的键:

```typescript
/******
 * Things that I couldn't make work.
 */
type Impossible<K extends keyof any> = {
  [P in K]: never;
};

type NoExtraProperties<T, U extends T = T> = U & Impossible<Exclude<keyof U, keyof T>>;

type Exactly<T, X> = T & Record<Exclude<keyof X, keyof T>, never>;


/*********
 * Attempted implementation
 */
const partial = <T, X extends Partial<T> = Partial<T>>(fn: (arg: T) => unknown, x: X): ((arg: Omit<T, keyof X>) => ReturnType<typeof fn>) => {
    return (arg) => fn(Object.assign(x, arg) as T);
}

// Example function to partially apply
const fn = ({name, age, alive}: {name: string, age: number, alive: boolean}): string => {
    return name + age.toString()
}

// plain function call
const fullCallResult = fn({
    name: "Maono",
    //sadfadsfdas: "sdfadsfa", // Error here, as expected.
    age: 19,
    alive: true
});

const partialllyApplied = partial(fn, {
    name: "asdfasdf",
    sadfadsfdas: "sdfadsfa" // <- No error here. I want this to give an error.
    //, age: 32
    //, alive: true
});

const partialllyAppliedResult = partialllyApplied({agde: 44, alive: false}) // <-- Error here, good, there is a type.

const partialllyAppliedResult2 = partialllyApplied({age: 44, alive: false})

如何使partial函数限制从传递的x对象中接受哪些键,模仿直接函数调用?


<details>
<summary>英文:</summary>

I want to partially apply an object to a function that accepts a single object as argument. I have been able to make the `partial` function below return a function that behaves as expected, but I am not able to make the partial function reject extra keys for the provided `x`:

```typescript
/******
 * Things that I couldn&#39;t make work.
 */
type Impossible&lt;K extends keyof any&gt; = {
  [P in K]: never;
};

type NoExtraProperties&lt;T, U extends T = T&gt; = U &amp; Impossible&lt;Exclude&lt;keyof U, keyof T&gt;&gt;;

type Exactly&lt;T, X&gt; = T &amp; Record&lt;Exclude&lt;keyof X, keyof T&gt;, never&gt;


/*********
 * Attempted implementation
 */
const partial = &lt;T, X extends Partial&lt;T&gt; = Partial&lt;T&gt;&gt;(fn: (arg: T) =&gt; unknown, x: X): ((arg: Omit&lt;T, keyof X&gt;) =&gt; ReturnType&lt;typeof fn&gt;) =&gt; {
    return (arg) =&gt; fn(Object.assign(x, arg) as T);
}

// Example function to partially apply
const fn = ({name, age, alive}: {name: string, age: number, alive: boolean}): string =&gt; {
    return name + age.toString()
}

// plain function call
const fullCallResult = fn({
    name: &quot;Maono&quot;,
    //sadfadsfdas: &quot;sdfadsfa&quot;, // Error here, as expected.
    age: 19,
    alive: true
});

const partialllyApplied = partial(fn, {
    name: &quot;asdfasdf&quot;,
    sadfadsfdas: &quot;sdfadsfa&quot; // &lt;- No error here. I want this to give an error.
    //, age: 32
    //, alive: true
});

const partialllyAppliedResult = partialllyApplied({agde: 44, alive: false}) // &lt;-- Error here, good, there is a type.

const partialllyAppliedResult2 = partialllyApplied({age: 44, alive: false})

How could you make the partial function restrict what keys are accepted from the passed x object mimicking a direct function call?

答案1

得分: 2

function partialApply<T, R, X extends Partial<T>>(
    fn: (arg: T) => R,
    def: X & { [K in keyof X]-?: K extends keyof T ? T[K] : never }
): (rest: { [K in keyof (Omit<T, keyof X> & Partial<T>)]: T[K] }) => R {
    return function apply(rest) {
        return fn({ ...def, ...rest })
    }
}

const fn = ({ name, age, alive }: { name: string, age: number, alive: boolean }): string => {
    return name + age.toString();
}
const apply1 = partialApply(fn, { age: 123 })

partialApply(fn, { age: 123 as 123 | undefined })

partialApply(fn, { age: 123 })({ alive: true })
英文:
function partialApply&lt;T, R, X extends Partial&lt;T&gt;&gt;(
    fn: (arg: T) =&gt; R,
    // X, but keyof T is not optional and unwanteds are never
    def: X &amp; { [K in keyof X]-?: K extends keyof T ? T[K] : never }
    //      T, but keyof X is optional
): (rest: { [K in keyof (Omit&lt;T, keyof X&gt; &amp; Partial&lt;T&gt;)]: T[K] }) =&gt; R {
    return function apply(rest) {
        // create new object, rest may override def
        return fn({ ...def, ...rest })
    }
}

// Example function to partially apply
const fn = ({ name, age, alive }: { name: string, age: number, alive: boolean }): string =&gt; {
    return name + age.toString();
}
const apply1 = partialApply(fn, { age: 123 })
//    ^?
// const apply1: (rest: {
//     name: string;
//     alive: boolean;
//     age?: number | undefined;
// }) =&gt; string

partialApply(fn, { age: 123 as 123 | undefined })
//                 ~~~ Type &#39;123 | undefined&#39; is not assignable to type &#39;123&#39;.
partialApply(fn, { age: 123 })({ alive: true })
//                             ~~~~~~~~~~~~~~~ Property &#39;name&#39; is missing in type &#39;{ alive: true; }&#39;

huangapple
  • 本文由 发表于 2023年7月11日 02:28:33
  • 转载请务必保留本文链接:https://go.coder-hub.com/76656388.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定