TypeScript函数,该函数采用没有某些键的对象

时间:2018-10-03 01:10:41

标签: typescript

尝试创建一个带有单个参数对象的函数,该对象必须没有一组键,而不必让人们手动指定类型。我试图以某种方式利用never,但是却陷入了试图理解对具有通用参数类型的函数参数进行推论的过程。

interface ReservedAttributes {
    someKey: string
}

// evaluates to never if the type has a key that intersects 
// with the keys in ReservedAttributes
type ValidAttributes<T> = keyof T extends Exclude<keyof T, keyof ReservedAttributes> ? T : never

// This is correctly a never, but it doesn't address this problem
type Test = ValidAttributes<{someKey : string}>


// This doesn't work because the function argument ends 
// up being inferred as { [name: string] : string}
function foo1<K extends { [name: string] : string}>(attributes: ValidAttributes<K>)  {
    // ...
}
foo1({a: 'hi', someKey: ''})

// Roughly equivalent to the above
function foo2< K extends { [name: string] : string}, V extends ValidAttributes<K> >(attributes: V)  {
    // ...
}

// This one allows V to correctly evaluate to never, 
// but I'm not sure how to leverage that
function foo3< K extends { [name: string] : string}, V extends ValidAttributes<K> >(attributes: K)  {
    // ...
}
foo3({a: 'hi', someKey: ''})

您将如何解决这个问题?

2 个答案:

答案 0 :(得分:5)

我想我首先会尝试避免使用泛型和条件类型,并执行以下操作:

type ValidAttributes = Record<string, string> &
  Partial<Record<keyof ReservedAttributes, never>>;
declare function foo(attributes: ValidAttributes): void;

foo({ a: 'hi' }); // okay
foo({ a: 'hi', someKey: '' }); // error, someKey

在这种情况下,ValidAttributes是一个通用的字符串值字典,但是来自ReservedAttributes的键被列为类型never的可选属性(允许丢失可选属性,并且类型never的属性实际上是不允许出现的,因此类型never的可选属性或多或少必须缺少。)这对您有用吗?

如果您需要在通用约束中使用条件类型,可以这样做:

type Attributes<K extends keyof any> = {
  [P in K]: P extends keyof ReservedAttributes ? never: string
};
declare function foo<T>(attributes: T & Attributes<keyof T>): void;
foo({ a: 'hi' }); // okay
foo({ a: 'hi', someKey: '' }) // error

但是它更复杂并且可以达到相似的结果。希望能有所帮助;祝你好运!

答案 1 :(得分:1)

一种可能的方法是让一个类型带有用void类型声明的可选属性。没有任何类型可分配给void,因此具有这些属性的任何对象都将与参数类型不兼容:

interface ReservedAttributes {
    someKey: string;
}

type DisallowedAttributes<T> = { [k in keyof T]?: void };


type AllowedKeys = string; // could be restricted if necessary 

function fn<K extends AllowedKeys>(t: { [k in K]: {}} & DisallowedAttributes<ReservedAttributes>) {

}

fn({ a: 'hi' }); // ok

fn({ b: 4, someKey: 'd' }); // Error: Type 'string' is not assignable to type 'void & {}'.
                            // Type 'string' is not assignable to type 'void'.