網站搜尋

oop - 如何使用介面取出物件的子集合?


假設我有這個類別和介面

class User {
    name: string;
    age: number;
    isAdmin: boolean;
}

interface IUser {
    name: string;
    age: number;
}

然後我從某處得到這個 json 物件

const data = {
    name: "John",
    age: 25,
    isAdmin: true
}

我想使用 IUser 來子集 data,並像這樣移除 isAdmin 屬性

let user = subset<IUser>(data);
// user is now { name: "John", age: 25 }
// can safely insert user in the db

我的問題是我該如何在 TypeScript 中實作那個函式?

function subset<T>(obj: object) {
    // keep all properties of obj that are in T
    // keep, all optional properties in T
    // remove any properties out of T
}

答案#1

沒有比這個更好的方法來做這件事:

function subset(obj: IUser) {
    return {
        name: obj.name,
        age: obj.age
    }
}

TypeScript 介面在執行時並不存在(也就是 subset 被調用的時候),所以你無法使用 IUser 介面來判斷哪些屬性是需要的,哪些不是。

你可以使用一個能夠「存活」於編譯過程中的類 但是:

class IUser {
    name: string;
    age: number;
}

編譯為:

var IUser = (function () {
    function IUser() {
    }
    return IUser;
}());

正如你所看到的,這些屬性並不是編譯後的輸出的一部分,因為類的成員僅會被添加到實例上,而不會添加到類上,所以即使是一個類也無法幫助你。

你可以使用裝飾器和元資料(這裡有更多相關內容),但對你的情境來說這聽起來有些過頭。

另一個更通用的 subset 函數選項是:

function subset<T>(obj: T, ...keys: (keyof T)[]) {
    const result = {} as T;

    keys.forEach(key => result[key] = obj[key]);
    return result;
}
let user1 = subset(data, "name", "age");
let user2 = subset(data, "name", "ag"); // error: Argument of type '"ag"' is not assignable to parameter of type '"name" | "age" | "isAdmin"'