英文:
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<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.
答案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 = '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
};
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 = '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
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。


评论