假设我有以下interfaces
。我想从那些objects
(例如interfaces
/ Actor
)声明Film
并将它们传递给一个类(From
)。类From
有一个方法Select
,我只希望它们接受传递对象(Actor
)的子项。我错了吗?有没有更好的办法?这不起作用,我不太了解它。
interface ColumnString {
maxLength: number
}
type ColumnType = ColumnString | typeof Number | typeof Date
interface Column {
toString: () => String
column: ColumnType
}
interface Table {
toString: () => string
columns: { [columnName: string]: Column }
}
const Actor: Table = {
toString: () => "Actor",
columns: {
FirstName: {
toString: () => "FirstName", column: { maxLength: 50 }
},
LastName: {
toString: () => "LastName", column: { maxLength: 50 }
},
BirthDate: {
toString: () => "BirthDate", column: Date
}
}
}
const Film: Table = {
toString: () => "Film",
columns: {
FilmTitle: {
toString: () => "FilmTitle", column: { maxLength: 100 }
},
Rating: {
toString: () => "Rating", column: { maxLength: 5 }
}
}
}
// This doesn't work
type TableColumn<T extends Table> = { [P in keyof T["columns"]]: Column }
class From1<T extends Table> {
private table: T
private columns: TableColumn<T>
constructor(table: T) {
this.table = table
}
public Print() {
console.log("Table:", this.table.toString())
Object.keys(this.columns).forEach((column, idx) => {
console.log("Column", idx+":", column.toString())
})
}
// This doesn't work.
public Select(...columns: TableColumn<T>[]) {
this.columns = columns
return this
}
}
function From<T extends Table>(table: T) {
return new From1(table)
}
const A = Actor.columns
const F = Film.columns
From(Actor)
// Should fail when I pass in F.FilmTitle but be OK with
// any Actor Column
.Select(A.FirstName, F.FilmTitle)
.Print()
我尝试了很多东西,但我不确定该怎么做。
提前致谢!
答案 0 :(得分:1)
您需要将列/表的名称作为字符串文字类型传递。然后,您可以使用不同字符串文字类型不兼容的事实,以确保您无法传递到其他表的Select
列。
我必须稍微更改类的结构,以获得name
属性,还有一些额外的初始化必须在表和列上完成,所以我不得不添加一些额外的功能,但结果实际上非常有用。
<强>用法强>
const Actor = table("Actor", {
FirstName: { maxLength: 50 },
LastName: { maxLength: 50 },
BirthDate: Date
});
const Film = table("Actor", {
FilmTitle: { maxLength: 50 },
Rating:{ maxLength: 50 },
});
const A = Actor.columns
const F = Film.columns
From(Actor)
// A.FirstName is ok, F.FilmTitle fails
.Select(A.FirstName, F.FilmTitle)
.Print()
<强>实施强>
type ColumnType = { maxLength: number } | (new () => Date); //For testing
interface Column<TName = string, TOwnerName = string> {
name: TName;
tableName : TOwnerName;
column: ColumnType;
toString(): string;
}
interface Table<TName, TColumns extends { [name: string]: Column }> {
name: TName;
toString(): string;
columns: TColumns
}
function table<TTableName extends string, TColumns extends { [name: string]: ColumnType }>(tableName: TTableName, columnTypes: TColumns) : Table<TTableName, { [P in keyof TColumns] : Column<P, TTableName>}> {
let columns :{ [P in keyof TColumns] : Column<P, TTableName>} = {} as any;
for(let key of Object.getOwnPropertyNames(columnTypes)) {
columns[key] = {
name: key,
toString : () => key,
column: columnTypes[key],
tableName: tableName
}
}
return {
name: tableName,
toString: () => tableName,
columns
};
}
class From1<TTableName extends string, TColumns extends { [name: string] : Column }> {
private table: Table<TTableName, TColumns>
private columns: Column<keyof TColumns, TTableName>[]
constructor(table: Table<TTableName, TColumns>) {
this.table = table
}
public Print() {
console.log("Table:", this.table.toString())
this.columns.forEach((column, idx) => {
console.log("Column", idx + ":", column.toString())
})
}
public Select<TColumnName extends keyof TColumns>(...columns: Column<TColumnName, TTableName>[]) {
this.columns = columns
return this
}
}
function From<TTableName extends string, TColumns extends { [name: string] : Column<string> }>(table: Table<TTableName, TColumns>) {
return new From1(table)
}