Type where object cannot have property

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

Type where object cannot have property

问题

I'm wondering if it's possible in TypeScript to provide some sort of intersection type that requires a type to not have a specific property. For my example, I have a bunch of models from the database.

type User = {
  id: number,
  name: string,
  email: string,
  created_at: Date,
  updated_at: Date,
  password: string
}

What I want is a way to define some ResultWithoutSensitiveData<T> type, which says "User CANNOT have created_at, updated_at, and password". Something that is basically Omit or Pick with keys instead of types. I've tried some form of conditional infer resulting in never, but I end up with compiler complaints about how undefined can't be assigned to type never. I just can't get it quite right.

I want the compiler and IDE to tell devs if they haven't removed sensitive properties.

英文:

I'm wondering if it's possible in TypeScript to provide some sort of intersection type that requires a type to not have a specific property. For my example, I have a bunch of models from the database.

type User = {
  id: number,
  name: string,
  email: string,
  created_at: Date,
  updated_at: Date,
  password: string
}

What I want is a way to define some ResultWithoutSensitiveData&lt;T&gt; type, which says "User CANNOT have created_at, updated_at, and password". Something that is basically Omit or Pick with keys instead of types. I've tried some form of conditional infer resulting in never, but I end up with compiler complaints about how undefined can't be assigned to type never. I just can't get it quite right.

I want the compiler and IDE to tell devs if they haven't removed sensitive properties.

答案1

得分: 1

Combine Omit and never:

type SensitiveKeys = 'created_at' | 'updated_at' | 'password';
type ResultWithoutSensitiveData<T> = Omit<T, SensitiveKeys> & {
  [key in SensitiveKeys]?: never; // the question mark is to make the keys optional to type T
};

你可以在 playground 上查看。

英文:

Combine Omit and never:

type SensitiveKeys = &#39;created_at&#39; | &#39;updated_at&#39; | &#39;password&#39;;
type ResultWithoutSensitiveData&lt;T&gt; = Omit&lt;T, SensitiveKeys&gt; &amp; {
  [key in SensitiveKeys]?: never; // the question mark is to make the keys optional to type T
};

You can have a look at the playground.

答案2

得分: 0

You need optional never. Depending on your exactOptionalPropertyTypes config they may look like password?: never or password?: undefined (as TS adds | undefined to all optional properties if exactOptionalPropertyTypes is off).

However, note that you can assign a value of type let x: {a: 1, password: 1} to let y: {a: 1}, which you can assign to let z: {a: 1, password?: never}, so a runtime check may be better. Generally, having validators for all the inputs and outputs of your server is recommended.

type CleanUser = {
  id: number,
  name: string,
  email: string,
  created_at?: never,
  updated_at?: never,
  password?: never,
}

type User = {
    id: number,
    name: string,
    email: string,
    created_at: Date,
    updated_at: Date,
    password: string
}

type sensitiveKey = 'created_at' | 'updated_at' | 'password';

type Pure<T> = T extends infer R ? {[K in keyof R]: T[K]} : T;

type ResultWithoutSensitiveData<T> = Pure<Omit<T, sensitiveKey> & Partial<Record<Extract<keyof T, sensitiveKey>, never>>>;

type User1 = ResultWithoutSensitiveData<User>;
//   ^?
// type User1 = {
//     id: number;
//     name: string;
//     email: string;
//     created_at?: undefined;
//     updated_at?: undefined;
//     password?: undefined;
// }
import { type } from "arktype";

const CleanUser = type(
    {
        id: "number",
        name: "string",
        email: "email"
    },
    { keys: "strict" }
)

CleanUser.assert({
    id: 123,
    name: "asd",
    email: "a@b.com",
    password: "blah"
})
// ^! runtime error
// ParseError: password must be removed
英文:

You need optional never.
Depending on your exactOptionalPropertyTypes config they may look like password?: never or password?: undefined (as TS adds | undefined to all optional properties if exactOptionalPropertyTypes is off)

Hovewer, note that you can assign value of type let x: {a: 1, password: 1} to let y: {a: 1}, which you can assign to let z: {a: 1, password?: never}, so a runtime check may be better
Generally, having validators for all the inputs and outputs of your server is recommented

type CleanUser = {
  id: number,
  name: string,
  email: string,
  created_at?: never,
  updated_at?: never,
  password?: never,
}
type User = {
    id: number,
    name: string,
    email: string,
    created_at: Date,
    updated_at: Date,
    password: string
}
type sensitiveKey = &#39;created_at&#39; | &#39;updated_at&#39; | &#39;password&#39;

type Pure&lt;T&gt; = T extends infer R ? {[K in keyof R]: T[K]} : T;

type ResultWithoutSensitiveData&lt;T&gt; = Pure&lt;Omit&lt;T, sensitiveKey&gt; &amp; Partial&lt;Record&lt;Extract&lt;keyof T, sensitiveKey&gt;, never&gt;&gt;&gt;

type User1 = ResultWithoutSensitiveData&lt;User&gt;
//   ^?
// type User1 = {
//     id: number;
//     name: string;
//     email: string;
//     created_at?: undefined;
//     updated_at?: undefined;
//     password?: undefined;
// }

import { type } from &quot;arktype&quot;

const CleanUser = type(
    {
        id: &quot;number&quot;,
        name: &quot;string&quot;,
        email: &quot;email&quot;
    },
    { keys: &quot;strict&quot; }
)

CleanUser.assert({
    id: 123,
    name: &quot;asd&quot;,
    email: &quot;a@b.com&quot;,
    password: &quot;blah&quot;
})
// ^! runtime error
// ParseError: password must be removed

huangapple
  • 本文由 发表于 2023年5月11日 17:53:12
  • 转载请务必保留本文链接:https://go.coder-hub.com/76226337.html
匿名

发表评论

匿名网友

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

确定