TypeScript NonNullablePartial type - Partial that doesn't allow explicit null/undefined values

m0rkklqb  于 4个月前  发布在  TypeScript
关注(0)|答案(2)|浏览(54)

搜索词

partial, nonnullable, required, undefined

建议

这可能看起来像#13195的重复问题,但这个问题与TS引擎不同,因为它不需要可能破坏性的更改。
相反,它利用了一个特殊的泛型Partial类型,该类型不允许显式设置null/undefined。

// these of course sadly don't work

type NonNullablePartial<T> = {
    [P in keyof T]?: NonNullable<T[P]>; 
};

type NonNullablePartial<T> = {
  [P in keyof T]?: T[P] extends null | undefined | infer R ? R : T[P];
};

我的建议通过防止可能的错误来增加类型安全性,而目前使用Partial的实现还不能做到这一点。

用例

这个问题的原因在于,显式的null / undefined阻止了将这些模式应用到"默认"参数上。
这个模式并不受此影响,一个显式设置的undefined会被重新分配给一个默认参数。然而,这种模式对于大量的配置变量来说是不切实际的。

function f({ message = 'message' } = {}) {
  console.log(message)
}

f({ message: undefined }); // message - correct!
f({ message: null });      // null  -  this makes sense, though not what I am looking for

换句话说:
我正在使用一个默认的配置对象,然后需要将其与用户的配置对象合并。我需要防止用户可能错误地将null / undefined值分配给参数,从而实际上替换并删除"默认"值。

示例

export interface ResponseErrorTemplate {
  code: string;
  status: number;
  message: string;
  errorConstructor: typeof ApolloError;
}

export interface ResponseErrorTemplateInput {
  message: string;
  errorConstructor: typeof ApolloError;
}

export type ResponseErrorTemplateGetter = (
  config?: Partial<ResponseErrorTemplateInput> // Here, keep Partial, but prevent null/undefined
) => ResponseErrorTemplate;

const defaultConfig: ResponseErrorTemplateInput = {
  message: 'Not found',
  errorConstructor: ApolloError,
};

export const getNotFoundTemplate: ResponseErrorTemplateGetter = config => ({
  ...defaultConfig,
  ...config, // if user defines message: undefined here, they replace the default 'Not found'
  status: 404,
  code: 'NOT_FOUND',
});

检查清单

我的建议满足以下准则:

  • 这不会对现有的TypeScript/JavaScript代码造成破坏性的更改
  • 这不会改变现有JavaScript代码的运行时行为
  • 这可以在不根据表达式的类型发出不同的JS的情况下实现
  • 这不是一个运行时特性(例如库功能、JavaScript输出的非ECMAScript语法等)
  • 这个特性将与TypeScript's Design Goals的其他部分保持一致。
abithluo

abithluo1#

我也需要这个。你可以看到Typescript文档中的示例是错误的:
https://www.typescriptlang.org/docs/handbook/utility-types.html#partialt

interface Todo {
    title: string;
    description: string;
}

function updateTodo(todo: Todo, fieldsToUpdate: Partial<Todo>) {
    return { ...todo, ...fieldsToUpdate };
}

const todo1 = {
    title: 'organize desk',
    description: 'clear clutter',
};

const todo2 = updateTodo(todo1, {
    description: 'throw out trash',
});

todo2 被推断为 Todo ,但生成的是 Partial<Todo>
你可以在 https://www.typescriptlang.org/play/index.html#code/JYOwLgpgTgZghgYwgAgCoHsAm7kG8CwAUMicmMGADYQBcyAzmFKAOYDcRpymE9CzAB3LoQdRsxDsiAXyJEYAVxAJhIZAoGY4kDNgAUYLOjq70AGmQxgESpnoYAqpu21kABThRycSgB5TAHwAlDSmBMSkUBBgClBquMgAdMmG2BbJiVY2do7OkMjSHISyhEQIIoxkRgCMyAC8eJyk5FSuAOToUCxwIMAAXig89ADWbWZNJEP8wELAInRtCNSeyEsKYJBQYzJFZRVgVdgATPXqeRCmBjUW4Vwt1HRKPFYgEJjjEZO807PzyG1gAAWUHQAHdkOh1mQoHB6IDtsUgrtCOUQPR0NREpR0CwrsckUA
如果你传递第二个参数为 undefined,你会得到一个 Partial<Todo>:

interface Todo {
    title: string;
    description: string;
}

function updateTodo(todo: Todo, fieldsToUpdate: Partial<Todo>):Todo{
    return { ...todo, ...fieldsToUpdate };
}

const todo1 = {
    title: 'organize desk',
    description: 'clear clutter',
};

const todo2 = updateTodo(todo1, {
    title: undefined,
    description: 'throw out trash',
});

console.log(todo2);

这是@ackvf所说的,但是使用Typescript文档中的示例。

使用场景:

拥有多语言应用,允许更改其中的一些内容,
setMessages(someMessages: PartialNotNull<Messages>) 警告,所以你不应该去undefined,因为这些消息将不会显示。

rdrgkggo

rdrgkggo2#

我也更新了我的留言,以便更好地解释意图。简而言之:
我需要阻止用户创建属性 undefined ,因为它们会替换默认配置对象中的配置值。

const obj: T = {  // obj = { message: undefined } - wrong
  ... { message: 'message' }, // default config object , type Partial<T>
  ... { message: undefined } // user config object, type Partial<T>, this shouldn't happen
}

相关问题