在 TypeScript 中,有没有办法限制 Partial<T> 的额外/多余属性?当类型是函数的参数时键入? [英] In TypeScript, is there a way to restrict extra/excess properties for a Partial<T> type when the type is a parameter to a function?

查看:14
本文介绍了在 TypeScript 中,有没有办法限制 Partial<T> 的额外/多余属性?当类型是函数的参数时键入?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

是否有标准方法可以让场景 1 因未指定已知属性而出现编译错误,就像在场景 2 中一样?或者有什么解决方法?

Is there a standard way to get Scenario 1 to have a compile error for not specifying known properties, just like in Scenario 2? Or is there some workaround?

class Class2 {
  g: number;
}

class Testing {
  static testIt3<T>(val: Partial<T>): void {
  }
}

const test = {
  g: 6,
  a: '6',
};

// Scenario 1
Testing.testIt3<Class2>(test);
// TS does not show any errors for this scenario

// Scenario 2
Testing.testIt3<Class2>({
  g: 6,
  a: '6',
});
// but it does for this scenario:
// Object literal may only specify known properties...

实时代码

推荐答案

类型系统不适用于对额外对象键的此类限制.TypeScript 中的类型不精确:如果对象的类型为 A,并且添加更多在A的定义中没有提到的属性,对象仍然是A类型.这本质上是支持类继承所必需的,其中子类可以向超类添加属性.

The type system isn't geared toward such restrictions on extra object keys. Types in TypeScript are not exact: if an object is of type A, and you add more properties to it that are not mentioned in the definition of A, the object is still of type A. This is essentially required to support class inheritance where subclasses can add properties to superclasses.

编译器将类型视为精确的唯一时间是当您使用新鲜"对象字面量(即尚未分配给任何内容的对象字面量)并将其传递给需要对象类型的内容时.这称为过度属性检查,它是排序语言中缺乏确切类型的解决方法.您希望对像 test 这样的非新鲜"对象进行过多的属性检查,但这不会发生.

The only time the compiler treats types as exact is when you are using a "fresh" object literal (that is, one that hasn't been assigned to anything yet) and passing it to something that expects an object type. This is called excess property checking and it is sort of a workaround for the lack of exact types in the language. You want excess property checking to occur with "non-fresh" objects like test, but that won't happen.

TypeScript 没有确切类型的具体表示;您不能采用 T 类型并从中生成 Exact.但是您可以使用 通用约束 来获得这种效果.给定一个 T 类型,以及一个 U 类型的对象,你希望它符合不可表示的 Exact 类型,你可以使这个:

TypeScript has no concrete representation for exact types; you can't take a type T and produce Exact<T> from it. But you can use a generic constraint to get this effect. Given a type T, and an object of type U that you want to conform to the unrepresentable Exact<T> type, you can make this:

type Exactly<T, U extends T> = {[K in keyof U]: K extends keyof T ? T[K] : never};
type IsExactly<T, U extends T> = U extends Exactly<T, U> ? true : false;

const testGood = {
    g: 1
}
type TestGood = IsExactly<Class2, typeof testGood>; // true

const testBad = {
    g: 6,
    a: '6',
};
type TestBad = IsExactly<Class2, typeof testBad>; // false

因此编译器能够判断 typeof testGoodExactly"而 typeof testBad 不是 正是.我们可以使用它来构建一个通用函数来做你想做的事.(在你的情况下,你想要像 ExactlyPartial 而不是 Exactly,但它非常相似......只是不要限制 U 扩展 T).

So the compiler is able to tell that typeof testGood is "Exactly<Class2, typeof testGood>" while typeof testBad is not Exactly<Class2, typeof testBad>. We can use this to build a generic function to do what you want. (In your case you want something like ExactlyPartial<T, U> instead of Exactly<T, U>, but it's very similar... just don't constrain U to extend T).

不幸的是,您的函数在 T 中已经是泛型的,该类型要精确.而你手动指定 T,泛型函数需要推断U的类型.TypeScript 不允许 部分类型参数推断.您必须手动指定函数中的所有类型参数,或者必须让编译器推断函数中的所有类型参数.所以有解决方法:

Unfortunately, your function is already generic in T, the type to make exact. And you are manually specifying T, the generic function needs to infer the type of U. TypeScript doesn't allow partial type parameter inference. You have to either manually specify all type parameters in a function, or you have to let the compiler infer all type parameters in a function. So there are workarounds:

一个是将您的函数拆分为一个 curred 函数,其中第一个泛型函数允许您指定 T 并且返回的泛型函数推断 U.它看起来像这样:

One is to split your function in to a curried function in which the first generic function lets you specify T and the returned generic function infers U. It looks like this:

    class Testing {
        static testIt<T>(): <U extends { [K in keyof U]:
            K extends keyof T ? T[K] : never
        }> (val: U) => void {
            return () => { }
        }
    }

    Testing.testIt<Class2>()(testBad); // error, prop "a" incompatible
    Testing.testIt<Class2>()(testGood); // okay

这如您所愿,但会影响运行时,因为您必须在运行时无缘无故地调用柯里化函数.

This works as you expect, but has runtime impact in that you have to call a curried function for no reason at runtime.

另一种解决方法是传递一个值,从中可以推断出 T 给函数.由于您不需要这样的值,因此这本质上是一个虚拟参数.同样,它具有运行时影响,因为您必须传入一个未使用的值.(您提到您实际上可能在运行时使用了这样的值,在这种情况下,这不再是一种解决方法,而是建议的解决方案,因为无论如何您都需要传入某些内容,以及 T 在您的代码示例中是一个红鲱鱼.)它看起来像这样:

Another workaround is to pass a value from which T can be inferred to the function. Since you don't need such a value, this is essentially a dummy parameter. Again, it has a runtime impact in that you have to pass in a value that is not used. (You mentioned that you might actually be using such a value at runtime, in which case, this is no longer a workaround but the suggested solution, since you need to pass in something anyway, and the manual specification of T in your code example was a red herring.) It looks like this:

    class Testing {
        static testIt<T, U extends { [K in keyof U]:
            K extends keyof T ? T[K] : never
        }>(ctor: new (...args: any) => T, val: U) {
            // not using ctor in here, so this is a dummy value                        
        }
    }

    Testing.testIt(Class2, testBad); // error, prop "a" incompatible
    Testing.testIt(Class2, testGood); // okay    

我能想到的第三个解决方法是只使用类型系统来表示柯里化函数返回的结果,而不实际调用它.它根本没有运行时影响,这使得它更适合为现有 JS 代码提供类型,但使用起来有点笨拙,因为您必须断言 Testing.testIt 以正确的方式运行.它看起来像这样:

The third workaround I can think of is to just use the type system to represent the result of the curried function return without actually calling it. It has no runtime impact at all, which makes it more amenable to giving types to existing JS code, but it's a bit clunky to use since you have to assert that Testing.testIt acts the right way. It looks like this:

    interface TestIt<T> {
        <U extends { [K in keyof U]: K extends keyof T ? T[K] : never }>(val: U): void;
    }

    class Testing {
        static testIt(val: object) {
            // not using ctor in here, so this is a dummy value                        
        }
    }

    (Testing.testIt as TestIt<Class2>)(testBad); // error, prop "a" incompatible
    (Testing.testIt as TestIt<Class2>)(testGood); // okay

<小时>

好的,希望其中之一对您有用.祝你好运!


Okay, hope one of those works for you. Good luck!

链接到代码

这篇关于在 TypeScript 中,有没有办法限制 Partial&lt;T&gt; 的额外/多余属性?当类型是函数的参数时键入?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆