TypeScript

TypeScript

TypeScript官網(wǎng) https://www.typescriptlang.org/zh/
TypeScript中文官網(wǎng) https://www.tslang.cn/samples/index.html

編程語言的類型分為動態(tài)類型語言(Dynamically Typed Language)和靜態(tài)類型語言(Statically Typed Language)浦译。
動態(tài)類型語言是在運行期間才會進行類型檢查的語言衔峰。類型對于變量,屬性,方法以及方法的返回類型都是可有可無的屡谐,在給變量賦值時才決定它的類型,之后還可以賦值不同類型的值疾棵,即使是基本類型黎侈。例如JavaScript,python涝桅,Ruby拜姿。
靜態(tài)類型語言是在編譯階段進行類型檢查。寫程序時要聲明變量的數(shù)據(jù)類型冯遂。例如C蕊肥,C++,Java债蜜。
TypeScript就是把不看重類型的動態(tài)語言變成關(guān)注類型的動態(tài)語言晴埂。是Javascript的超集,提供從es6到es10寻定,甚至是esnext的語法支持儒洛。兼容各種瀏覽器、系統(tǒng)狼速,完全開源琅锻。

為什么使用TypeScript?
  1. 程序更容易理解
    對于確定函數(shù)或者方法輸入輸出的參數(shù)類型向胡,外部條件等恼蓬,動態(tài)類型語言需要手動調(diào)試等過程,例如debugger僵芹、console处硬。有了TypeScript就很容易確定以上內(nèi)容。
  2. 效率更高
    在不同的代碼塊和定義中跳轉(zhuǎn)拇派,代碼自動補全荷辕。
  3. 更少的錯誤
    編譯期間就能夠發(fā)現(xiàn)大部分錯誤,避免一些常見錯誤(property 'xxxx' undefined)
  4. 非常好的包容性
    完全兼容JavaScript件豌,第三方庫可以單獨編寫類型文件疮方。

但會增加一定程度上的學(xué)習(xí)成本,短期內(nèi)增加了一些開發(fā)成本茧彤。所以TS適用于一些中大型長期開發(fā)維護的項目骡显。

TypeScript安裝

安裝node,或確定已安裝的node的版本至少為10.0.0以上。
全局安裝TypeScript惫谤,運行npm install -g typescript壁顶。安裝完成之后輸入tsc -v可查看版本號。(tsc 代表 TypeScript 編譯器溜歪,只要編譯器運行博助,它將在項目文件夾中查找名為tsconfig.json 的文件。)


typescript安裝
基礎(chǔ)類型
原始數(shù)據(jù)類型(primitive values):

Boolean痹愚、Null富岳、Undefined、Number拯腮、BigInt(es6新增)窖式、String、Symbo

let isDone:boolean = false

let age:number = 10

let firstName:string = 'vivid'
let message:string = `Hello,${firstName}`

let u:undefined = undefined
let n:null = null

//undefined和null是所有類型的子類型
let num:number = undefined

注:null and undefined
null表示"沒有對象"动壤,即該處不應(yīng)該有值萝喘,轉(zhuǎn)為數(shù)值時為0。 在內(nèi)存里的表示就是琼懊,棧中的變量沒有指向堆中的內(nèi)存對象阁簸。
1、作為函數(shù)的參數(shù)哼丈,表示該函數(shù)的參數(shù)不是對象
2启妹、作為對象原型鏈的終點

undefined表示"缺少值",就是此處應(yīng)該有一個值醉旦,但是還沒有定義饶米,轉(zhuǎn)為數(shù)值時為NaN。
1车胡、變量被聲明了檬输,但沒有賦值時,就等于undefined
2匈棘、調(diào)用函數(shù)時丧慈,應(yīng)該提供的參數(shù)沒有提供,該參數(shù)等于undefined
3主卫、對象沒有賦值的屬性逃默,該屬性的值為undefined
4、函數(shù)沒有返回值時队秩,默認(rèn)返回undefined

console.log(typeof (null))// object
console.log(typeof (undefined))// undefined

Number(null)//0
Number(undefined)// NaN

null == undefined// true
null === undefined// false

//變量被聲明笑旺,但沒有賦值
var a
console.log(a)// undefined
//函數(shù)定義了形參昼浦,但沒有傳遞實參
function fn(a) {
    console.log(a)// undefined
}
fn()
//引用對象中不存在的屬性
console.log(Object.a); // undefined
//函數(shù)沒有返回值
var a = fn()
console.log(a)// undefined
Any和unkonw

有時候馍资,我們會想要為那些在編程階段還不清楚類型的變量指定一個類型。 這些值可能來自于動態(tài)的內(nèi)容,比如來自用戶輸入或第三方代碼庫鸟蟹。 這種情況下乌妙,我們不希望類型檢查器對這些值進行檢查而是直接讓它們通過編譯階段的檢查。 那么我們可以使用 any類型來標(biāo)記這些變量:

let notSure:any = 4
notSure = 'maybe a string'
notSure = true
notSure = {Hello: () => {}}

譯者: any 和 unknown 的最大區(qū)別是, unknown 是 top type (任何類型都是它的 subtype) , 而 any 即是 top type, 又是 bottom type (它是任何類型的 subtype ) , 這導(dǎo)致 any 基本上就是放棄了任何類型檢查建钥。

對照于 any藤韵,unknown 是類型安全的。 任何值都可以賦給 unknown熊经,但是當(dāng)沒有類型斷言或基于控制流的類型細(xì)化時 unknown 不可以賦值給其它類型泽艘,除了它自己和 any 外。 同樣地镐依,在 unknown 沒有被斷言或細(xì)化到一個確切類型之前匹涮,是不允許在其上進行任何操作的。

//任何值都可以賦給 unknown
let uncertain:unknown = 5
uncertain = 'is a string'
uncertain = {Hello: () => {}}
// unknown 不可以賦值給其它類型槐壳,除了它自己和 any 外
notSure = uncertain
let isDone:boolean = false
isDone = uncertain//Type 'unknown' is not assignable to type 'boolean'.
//簡單應(yīng)用
const isFalsy = (value: unknown) => (value === 0 ? false : !value);
console.log(isFalsy(''))//true
Array和Tuple

Array 有兩種方式可以定義數(shù)組然低。 第一種,可以在元素類型后面接上 []务唐,表示由此類型元素組成的一個數(shù)組:

//數(shù)組的項不允許出現(xiàn)其他類型
let arrOfNumber: number[] = [1,2,3]
//數(shù)組的一些方法的參數(shù)也會根據(jù)數(shù)組在定義時約定的類型進行限制:
arrOfNumbers.push(3)
arrOfNumbers.push('abc')

第二種方式是使用數(shù)組泛型雳攘,Array<元素類型>:

let arrOfString: Array<String> = ['abc','def','g']

Tuple(元組)
元組類型允許表示一個已知元素數(shù)量和類型的數(shù)組,各元素的類型不必相同枫笛。 其本質(zhì)還是一個數(shù)組吨灭,可以使用一些數(shù)組的方法。

let user:[string,number] = ['jack',29]
//數(shù)組中添加的項只能是定義的類型
user.push('man')
user.push(true)//Error, 布爾不是(string | number)類型
Interfaces 接口

對對象的類型進行描述刑巧,也被成為“鴨子類型”沃于。只在ts中作靜態(tài)檢查。

interface Person{
    name: string,
    age: Number
}
let rose: Person = {
    name: 'rose',
    age: 20
}
function userUser(infor:Person){
    console.log('user')
}
userUser({name: 'rose',age: 20})

可選屬性:接口里的屬性不全都是必需的海诲。 有些是只在某些條件下存在繁莹,或者根本不存在。

//age是可選或者不選的
interface Person{
    name: string,
    age?: Number
}
let rose: Person = {
    name: 'rose',
}

只讀屬性:一些對象屬性只能在對象剛剛創(chuàng)建的時候修改其值特幔。 你可以在屬性名前用 readonly來指定只讀屬性咨演。

interface Person{
    readonly id: number,
    name: string,
    age?: Number
}
let rose: Person = {
    id: 1,
    name: 'rose',
}
rose.id = 2//Error

//TypeScript具有ReadonlyArray<T>類型,它與Array<T>相似
let readonlyArr: ReadonlyArray<Number> = [1,2,3,4]
readonlyArr[0] = 6//Error,Index signature in type 'readonly Number[]' only permits reading

readonly vs const
最簡單判斷該用readonly還是const的方法是看要把它做為變量使用還是做為一個屬性蚯斯。 做為變量使用的話用 const薄风,若做為屬性則使用readonly。

Function 函數(shù)

我們可以給每個參數(shù)添加類型之后再為函數(shù)本身添加返回值類型拍嵌。 TypeScript能夠根據(jù)返回語句自動推斷出返回值類型遭赂,因此我們通常省略它。

//z為可選參數(shù)横辆,可選參數(shù)后面不能添加確定參數(shù)
function add(x:number,y:number,z?:number):number{
    if (typeof z === 'number') {
        return x+y+z
    }else{
        return x+y
    }
}
add(1,2)
add(1,2,3)
//let add =  (x: number, y: number): number => { return x + y; };
let add2: (x:number,y:number,z?:number) => number = add// ts中冒號后面都是在聲明類型
//另一種寫法
interface sum {
    (x:number,y:number,z?:number): number
}
let add3:sum = add
Union Types 聯(lián)合類型撇他、Type assertions 類型斷言
//我們只需要用中豎線來分割兩個
let utype: number | string 

//這里我們可以用 as 關(guān)鍵字,告訴typescript 編譯器,
//你沒法判斷我的代碼困肩,但是我本人很清楚划纽,這里我就把它看作是一個 string,你可以給他用 string 的方法锌畸。
function getLength(input:string|number):number{
    const str = input as string
    if (str.length){
        return str.length
    }else{
        const number = input as number
        return number.toString().length
    }
}

//type guard類型守衛(wèi)
function getLength2(input:string|number):number{
    if (typeof input === 'string'){
        return input.length
    }else{
        return input.toString().length
    }
}
Class 類
  • public 修飾的屬性或方法是公有的勇劣,可以在任何地方被訪問到,默認(rèn)所有的屬性和方法都是 public 的
  • private 修飾的屬性或方法是私有的潭枣,不能在聲明它的類的外部訪問
  • protected 修飾的屬性或方法是受保護的比默,它和 private 類似,區(qū)別是它在子類中也是允許被訪問的
class Animal {
    name: string;
    constructor(name){
        this.name = name
    }
    run(){
        return `${this.name} is running`
    }
    private eat(){
        return `${this.name} is eating`
    }
}
const snake = new Animal('snake')
console.log(snake.run())
console.log(snake.eat())// Error

class Dog extends Animal{
    dark(){
        return `${this.name} is barking`
    }
}
const xiaobei = new Dog('xiaobei')
console.log(xiaobei.run())
console.log(xiaobei.dark())

class Cat extends Animal{
    static categories = ['mammal']
    constructor(name){
        super(name)
        console.log(this.name)
    }
    run(){
        return 'Meow,'+super.run()
    }
}
const maomao = new Cat('maomao')
console.log(maomao.run())
console.log(Cat.categories)
Class 類和Interface接口

同類之間有共同的特性盆犁,可以把這些特性提取為接口退敦,可以使用implement來實現(xiàn)接口。

//類和接口
// class Car {
//   switchRadio(trigger:boolean){}
// }
// class CellPhone {
//   switchRadio(trigger:boolean){}
// }

interface Radio {
  switchRadio(trigger:boolean): void;
}
interface Barrery {
  checkBarrery(trigger:boolean): void;
}
interface RadioBattery extends Barrery {
  checkBarrery(trigger:boolean): void;
}
class Car implements Radio{
  switchRadio(trigger:boolean){}
}
class CellPhone  implements Radio,Barrery{
    switchRadio(trigger:boolean){}
    checkBarrery(trigger:boolean){}
 }
Enum 枚舉

一般會使用const來定義一些常量蚣抗,但有些取值是再范圍內(nèi)的一系列常量侈百,例如紅黃藍(lán)、上下左右翰铡,這時候就可以用枚舉來定義钝域。

 // 數(shù)字枚舉
// 沒有定義初值時,初始值為0锭魔,其余的成員會從 0開始自動增長
enum Direction {
  Up,
  Down,
  Left,
  Right
}
console.log(Direction.Up);// 0
console.log(Direction[0]);// Up
//若定義一個數(shù)字枚舉例证,Up 的初始值為10,自動增長Down為11迷捧,Left為12织咧,Right為13
enum Direction {
  Up = 10,
  Down,
  Left,
  Right
}
console.log(Direction.Up);// 10
console.log(Direction[11]);// Down

// 字符串枚舉
// 在一個字符串枚舉里,每個成員都必須用字符串字面量漠秋,或另外一個字符串枚舉成員進行初始化笙蒙。字符串枚舉沒有自增長的行為。
enum Direction {
  Up = 'Up',
  Down = 'Down',
  Left = 'Left',
  Right = 'Right'
}

// 常量枚舉
// 為了避免在額外生成的代碼上的開銷和額外的非直接的對枚舉成員的訪問庆锦,我們可以使用 const枚舉
const enum Direction {
  Up = 'Up',
  Down = 'Down',
  Left = 'Left',
  Right = 'Right'
}
Generics 泛型
function echo(arg:any):any{
  return arg
}
const result = echo(123)

上面的例子使用any類型會導(dǎo)致這個函數(shù)可以接收任何類型的arg參數(shù)捅位,這樣就丟失了一些信息:傳入的類型與返回的類型應(yīng)該是相同的。如果我們傳入一個數(shù)字搂抒,我們只知道任何類型的值都有可能被返回艇搀。
泛型在定義函數(shù)類的時候,可以不先指定類型求晶,而是可以在使用的時候指定類型焰雕。

//給函數(shù)echo類型變量T。 T幫助我們捕獲用戶傳入的類型(比如:number)
function echo<T>(arg: T): T{
  return arg;
}
const result = echo('123')
function swap <T,U> (tuple:[T,U]): [U,T]{
  return [tuple[1],tuple[0]]
}
const res = swap([1,'123'])

泛型約束

function echoWithArray<T>(arg:T):T{
  console.log(arg.length) // Error: T doesn't have .length
  return arg
}

相比于操作any所有類型芳杏,我們想要限制函數(shù)去處理任意帶有.length屬性的所有類型矩屁。 只要傳入的類型有這個屬性辟宗,我們就允許,就是說至少包含這一屬性档插。 為此,我們需要列出對于T的約束要求亚再。

function echoWithArray<T>(arg:T[]):T[]{
  return arg
}

//更好的解決方法
function echoWithArray<T extends IWithLength>(arg:T):T{
  console.log(arg.length)
  return arg
}
interface IWithLength{
  length: number
}

const res2 = echoWithArray([1,2,3])
const res3 = echoWithArray({length: 10})

泛型在類和接口中的使用

class Queue<T> {
  private data = [];
  push(item:T){
    return this.data.push(item);
  }
  pop():T{
    return this.data.shift();
  }
}
const queue = new Queue<number>();
queue.push(1)
console.log(queue.pop().toFixed())

interface keyPair<T,U>{
  key: T,
  value: U
}
let kp1: keyPair<number,string> = {key: 1,value: '1'}
type alias類型別名

類型別名(type aliases)為一個類型創(chuàng)建一個新的名字郭膛。有些時候類型別名與接口(interfaces)很相似,但是類型別名可以用于聲明原始類型(primitives)氛悬,聯(lián)合類型(unions)则剃,元組類型(tuples),還有其它一些你必須要手寫的類型如捅。
type alias與interface的區(qū)別

// 1棍现、聲明對象和函數(shù)類型的語法不一樣
interface A {
  x: number;
  y: string;
}
interface B {
  (m:number,n:string): void
}
type A = {
  x: number;
  y: string;
}
type B = (m:number,n:string): => void

//2、類型別名(type alias)可以用來聲明一些接口(interface)無法聲明的其他類型
// 聲明已有類型(即取別名)
type A = number;
// 字面量類型
type B = 'foo';
// 元組類型
type C = [number, string];
// 聯(lián)合類型
type D = number | boolean;
type Directions = 'UP' | 'DOWN' | 'LEFT' | 'RIGHT'
const toWhere:Directions = 'DOWN'
// 交叉類型
type E = A & D;

interface IName {
  name: string
}
type IPerson = IName & {age:number}
const amy: IPerson = {name: 'amy',age: 20}

// 3镜遣、接口(interface)可以通過合并不同的聲明進行不斷擴展
interface A {
  x: number;
}
interface A {
  y: string;
}
// 經(jīng)過上面兩次聲明后 A 的類型為: { x: number; y: number; }

// 4己肮、接口(interface)通過 extends 關(guān)鍵字來擴展一個類型(這個類型可以是 interface, 也可以是 type alias),類型別名(type alias)則通過交叉類型來實現(xiàn)擴展
interface Super {
  a: string;
}
interface Sub extends Super {
  b: number;
}
// type alias
type Super = {
  a: string;
};
type Sub = Super & { 
  b: number 
};
聲明文件

在引用第三方庫的時候悲关,需要聲明文件谎僻。在xxx.d.ts文件中聲明

declare var JQuery: (selector: string) => any
//ts中的JQuery('#foo')就不會報錯

但我們不可能一個個去定義,所以在TypeScript 2.0以上的版本寓辱,獲取類型聲明文件只需要使用npm艘绍。例如獲取JQuery的聲明文件

npm install --save @types/jquery

TypeScript查詢對應(yīng)包的類型文件可在https://microsoft.github.io/TypeSearch/查詢。有些在安裝的時候秫筏,直接提供定義文件和源代碼诱鞠,安裝包里有xxx.d.ts就說明有定義文件。

內(nèi)置對象

根據(jù)標(biāo)準(zhǔn)在全局作用域(global)上存在的對象这敬,這里的標(biāo)準(zhǔn)是指ECMAScript和其他的環(huán)境(eg:DOM的標(biāo)準(zhǔn))航夺。在每個ts項目中會自動的加載這些對象。

//ECMAScript標(biāo)準(zhǔn)提供的內(nèi)置對象
const date = new Date();
date.getDate();
const reg = /[a-z]/
reg.test('ab')

// build-in Object
Math.random()

// DOM and BOM崔涂,提供的內(nèi)置對象有Document敷存、HTMLElement、Event堪伍、NodeList 等
let body: HTMLElement = document.body;
let allLis:NodeListOf<HTMLLIElement> = document.querySelectorAll('li');
allLis.keys();
document.addEventListener('click',(e)=>{
  e.preventDefault();
})

Utility Types
Utility Types是ts內(nèi)置的實用類型锚烦,用于類型轉(zhuǎn)換。常用的類型有:Partial帝雇, Required涮俄, Readonly, Record<K,T>尸闸,Pick<T,K>彻亲,Omit<T,K>孕锄,Exclude<T,U>等。
文檔鏈接:https://www.typescriptlang.org/docs/handbook/utility-types.html

//Partial<T>
//將泛型傳入的T中所有屬性轉(zhuǎn)換為可選屬性苞尝,返回的類型可以是T的任意子集畸肆。
interface UPerson {
  name: string,
  age: number,
  sex: boolean
}
const vivi = {name: 'vivi',age: 22,sex:true}
//相當(dāng)于type IPartial = {name?: string; age?: number;sex?:boolean}
type IPartial = Partial<UPerson>
const vivi2:IPartial = {name: 'vivi'}

//Required<T>
//將泛型傳入的T中所有屬性轉(zhuǎn)換為必須屬性,和Partial類型相反宙址。
type IRequired = Required<UPerson>
const vivi3:IRequired = {age: 20}// Error. Property 'name' is missing in type '{ age: number; }' but required in type 'Required<UPerson>'

//Pick<T,K>
//通過傳入的泛型T中選擇一組屬性K(字符串字面值或字符串字面值的聯(lián)合)來構(gòu)造類型轴脐。
//相當(dāng)于type IPick = {name: string;sex: boolean;}
type IPick = Pick<UPerson,'name' | 'sex'>
const vivi4:IPick = {name: 'vivi',sex: true}

//Omit<T,K>
//通過傳入的泛型T中選擇一組屬性K并刪除其他屬性,和Pick相反抡砂。
type Iomit = Omit<UPerson,'name'>
const vivi5:IPartial = {age: 20,sex: true}

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末大咱,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子注益,更是在濱河造成了極大的恐慌碴巾,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,548評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件丑搔,死亡現(xiàn)場離奇詭異厦瓢,居然都是意外死亡,警方通過查閱死者的電腦和手機啤月,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,497評論 3 399
  • 文/潘曉璐 我一進店門旷痕,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人顽冶,你說我怎么就攤上這事欺抗。” “怎么了强重?”我有些...
    開封第一講書人閱讀 167,990評論 0 360
  • 文/不壞的土叔 我叫張陵绞呈,是天一觀的道長。 經(jīng)常有香客問我间景,道長佃声,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,618評論 1 296
  • 正文 為了忘掉前任倘要,我火速辦了婚禮圾亏,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘封拧。我一直安慰自己志鹃,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 68,618評論 6 397
  • 文/花漫 我一把揭開白布泽西。 她就那樣靜靜地躺著曹铃,像睡著了一般。 火紅的嫁衣襯著肌膚如雪捧杉。 梳的紋絲不亂的頭發(fā)上陕见,一...
    開封第一講書人閱讀 52,246評論 1 308
  • 那天秘血,我揣著相機與錄音,去河邊找鬼评甜。 笑死灰粮,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的忍坷。 我是一名探鬼主播粘舟,決...
    沈念sama閱讀 40,819評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼承匣!你這毒婦竟也來了蓖乘?” 一聲冷哼從身側(cè)響起锤悄,我...
    開封第一講書人閱讀 39,725評論 0 276
  • 序言:老撾萬榮一對情侶失蹤韧骗,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后零聚,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體袍暴,經(jīng)...
    沈念sama閱讀 46,268評論 1 320
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,356評論 3 340
  • 正文 我和宋清朗相戀三年隶症,在試婚紗的時候發(fā)現(xiàn)自己被綠了政模。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,488評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡蚂会,死狀恐怖淋样,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情胁住,我是刑警寧澤趁猴,帶...
    沈念sama閱讀 36,181評論 5 350
  • 正文 年R本政府宣布,位于F島的核電站彪见,受9級特大地震影響儡司,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜余指,卻給世界環(huán)境...
    茶點故事閱讀 41,862評論 3 333
  • 文/蒙蒙 一捕犬、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧酵镜,春花似錦碉碉、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,331評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至缸濒,卻和暖如春足丢,著一層夾襖步出監(jiān)牢的瞬間粱腻,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,445評論 1 272
  • 我被黑心中介騙來泰國打工斩跌, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留绍些,地道東北人。 一個月前我還...
    沈念sama閱讀 48,897評論 3 376
  • 正文 我出身青樓耀鸦,卻偏偏與公主長得像柬批,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子袖订,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,500評論 2 359

推薦閱讀更多精彩內(nèi)容